You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by rm...@apache.org on 2018/10/04 22:16:36 UTC

[3/3] metron git commit: METRON-1771 Update REST endpoints to support eventually consistent UI updates (merrimanr) closes apache/metron#1190

METRON-1771 Update REST endpoints to support eventually consistent UI updates (merrimanr) closes apache/metron#1190


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/de533063
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/de533063
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/de533063

Branch: refs/heads/master
Commit: de533063c26c8a2462dbfff33228a949c4a96c97
Parents: 7e222fa
Author: merrimanr <me...@gmail.com>
Authored: Thu Oct 4 17:16:18 2018 -0500
Committer: rmerriman <me...@gmail.com>
Committed: Thu Oct 4 17:16:18 2018 -0500

----------------------------------------------------------------------
 metron-interface/metron-rest/README.md          |  16 ++
 .../rest/controller/MetaAlertController.java    |  18 +-
 .../rest/controller/UpdateController.java       |  31 ++-
 .../metron/rest/service/MetaAlertService.java   |  12 +-
 .../metron/rest/service/UpdateService.java      |   9 +-
 .../rest/service/impl/MetaAlertServiceImpl.java |  18 +-
 .../rest/service/impl/UpdateServiceImpl.java    |  18 +-
 .../MetaAlertControllerIntegrationTest.java     | 152 +++++++-------
 .../elasticsearch/dao/ElasticsearchDao.java     |  32 +--
 .../dao/ElasticsearchMetaAlertDao.java          |  37 ++--
 .../dao/ElasticsearchMetaAlertUpdateDao.java    |  55 ++---
 .../dao/ElasticsearchUpdateDao.java             |  42 ++--
 .../dao/ElasticsearchMetaAlertDaoTest.java      |  27 ++-
 .../dao/ElasticsearchUpdateDaoTest.java         |  52 +++++
 .../apache/metron/indexing/dao/HBaseDao.java    |  31 +--
 .../metron/indexing/dao/MultiIndexDao.java      | 138 ++++++++-----
 .../dao/metaalert/MetaAlertCreateResponse.java  |  40 ----
 .../dao/metaalert/MetaAlertUpdateDao.java       |  20 +-
 .../AbstractLuceneMetaAlertUpdateDao.java       |  81 +++++++-
 .../metron/indexing/dao/update/UpdateDao.java   |  25 ++-
 .../InMemoryMetaAlertRetrieveLatestDao.java     |  49 +++++
 .../metron/indexing/dao/HBaseDaoTest.java       |  41 ++++
 .../apache/metron/indexing/dao/InMemoryDao.java |  18 +-
 .../indexing/dao/InMemoryMetaAlertDao.java      | 199 +++++--------------
 .../dao/InMemoryMetaAlertUpdateDao.java         |  91 +++++++++
 .../metron/indexing/dao/MultiIndexDaoTest.java  |  96 +++++++++
 .../metron/indexing/dao/UpdateDaoTest.java      |  74 +++++++
 .../indexing/dao/UpdateIntegrationTest.java     | 138 +++++++------
 .../dao/metaalert/MetaAlertIntegrationTest.java | 119 ++++++++---
 .../AbstractLuceneMetaAlertUpdateDaoTest.java   |  56 ++++--
 .../integration/HBaseDaoIntegrationTest.java    |   7 +-
 .../org/apache/metron/solr/dao/SolrDao.java     |  28 +--
 .../metron/solr/dao/SolrMetaAlertDao.java       |  49 +++--
 .../metron/solr/dao/SolrMetaAlertUpdateDao.java |  38 ++--
 .../apache/metron/solr/dao/SolrUpdateDao.java   |  36 ++--
 .../metron/solr/dao/SolrMetaAlertDaoTest.java   |  21 +-
 .../metron/solr/dao/SolrUpdateDaoTest.java      |  19 +-
 37 files changed, 1206 insertions(+), 727 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-interface/metron-rest/README.md
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/README.md b/metron-interface/metron-rest/README.md
index 2c216d1..2ce9522 100644
--- a/metron-interface/metron-rest/README.md
+++ b/metron-interface/metron-rest/README.md
@@ -336,6 +336,8 @@ Request and Response objects are JSON formatted.  The JSON schemas are available
 | [ `GET /api/v1/storm/supervisors`](#get-apiv1stormsupervisors)|
 | [ `PATCH /api/v1/update/patch`](#patch-apiv1updatepatch)|
 | [ `PUT /api/v1/update/replace`](#put-apiv1updatereplace)|
+| [ `POST /api/v1/update/add/comment`](#put-apiv1updateaddcomment)|
+| [ `POST /api/v1/update/remove/comment`](#put-apiv1updateremovecomment)|
 | [ `GET /api/v1/user`](#get-apiv1user)|
 
 ### `POST /api/v1/alerts/ui/escalate`
@@ -963,6 +965,20 @@ Request and Response objects are JSON formatted.  The JSON schemas are available
         ```
   * Returns:
     * 200 - Current user
+    
+### `POST /api/v1/update/add/comment`
+  * Description: Add a comment to an alert
+  * Input:
+    * request - Comment add request
+  * Returns:
+    * 200 - Returns the complete alert document with comments added.
+    
+### `POST /api/v1/update/remove/comment`
+  * Description: Remove a comment from an alert
+  * Input:
+    * request - Comment remove request
+  * Returns:
+    * 200 - Returns the complete alert document with comments removed.
 
 ### `GET /api/v1/user`
   * Description: Retrieves the current user

http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/MetaAlertController.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/MetaAlertController.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/MetaAlertController.java
index d42403a..69b1779 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/MetaAlertController.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/MetaAlertController.java
@@ -24,8 +24,8 @@ import io.swagger.annotations.ApiResponse;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertStatus;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertAddRemoveRequest;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertCreateRequest;
-import org.apache.metron.indexing.dao.metaalert.MetaAlertCreateResponse;
 import org.apache.metron.indexing.dao.search.SearchResponse;
+import org.apache.metron.indexing.dao.update.Document;
 import org.apache.metron.rest.RestException;
 import org.apache.metron.rest.service.MetaAlertService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -57,9 +57,9 @@ public class MetaAlertController {
   @ApiOperation(value = "Creates a new meta alert from a list of existing alerts.  "
       + "The meta alert status will initially be set to 'ACTIVE' and summary statistics "
       + "will be computed from the list of alerts.  A list of groups included in the request are also added to the meta alert.")
-  @ApiResponse(message = "The GUID of the new meta alert", code = 200)
+  @ApiResponse(message = "Returns the complete document of the created metaalert.", code = 200)
   @RequestMapping(value = "/create", method = RequestMethod.POST)
-  ResponseEntity<MetaAlertCreateResponse> create(
+  ResponseEntity<Document> create(
       @ApiParam(name = "createRequest", value = "Meta alert create request which includes a list of alert "
           + "get requests and a list of custom groups used to annotate a meta alert", required = true)
       @RequestBody  final MetaAlertCreateRequest createRequest
@@ -68,9 +68,9 @@ public class MetaAlertController {
   }
 
   @ApiOperation(value = "Adds an alert to an existing meta alert.  An alert will not be added if it is already contained in a meta alert.")
-  @ApiResponse(message = "Returns 'true' if the alert was added and 'false' if the meta alert did not change.", code = 200)
+  @ApiResponse(message = "Returns the complete metaalert document with the alerts added.", code = 200)
   @RequestMapping(value = "/add/alert", method = RequestMethod.POST)
-  ResponseEntity<Boolean> addAlertsToMetaAlert(
+  ResponseEntity<Document> addAlertsToMetaAlert(
       @ApiParam(name = "metaAlertAddRemoveRequest", value = "Meta alert add request which includes a meta alert GUID and list of alert get requests", required = true)
       @RequestBody  final MetaAlertAddRemoveRequest metaAlertAddRemoveRequest
   ) throws RestException {
@@ -78,9 +78,9 @@ public class MetaAlertController {
   }
 
   @ApiOperation(value = "Removes an alert from an existing meta alert.  If the alert to be removed is not in a meta alert, 'false' will be returned.")
-  @ApiResponse(message = "Returns 'true' if the alert was removed and 'false' if the meta alert did not change.", code = 200)
+  @ApiResponse(message = "Returns the complete metaalert document with the alerts removed.", code = 200)
   @RequestMapping(value = "/remove/alert", method = RequestMethod.POST)
-  ResponseEntity<Boolean> removeAlertsFromMetaAlert(
+  ResponseEntity<Document> removeAlertsFromMetaAlert(
       @ApiParam(name = "metaAlertAddRemoveRequest", value = "Meta alert remove request which includes a meta alert GUID and list of alert get requests", required = true)
       @RequestBody  final MetaAlertAddRemoveRequest metaAlertAddRemoveRequest
   ) throws RestException {
@@ -88,9 +88,9 @@ public class MetaAlertController {
   }
 
   @ApiOperation(value = "Updates the status of a meta alert to either 'ACTIVE' or 'INACTIVE'.")
-  @ApiResponse(message = "Returns 'true' if the status changed and 'false' if it did not.", code = 200)
+  @ApiResponse(message = "Returns the complete metaalert document with the updated status.", code = 200)
   @RequestMapping(value = "/update/status/{guid}/{status}", method = RequestMethod.POST)
-  ResponseEntity<Boolean> updateMetaAlertStatus(
+  ResponseEntity<Document> updateMetaAlertStatus(
       final @ApiParam(name = "guid", value = "Meta alert GUID", required = true)
       @PathVariable String guid,
       final @ApiParam(name = "status", value = "Meta alert status with a value of either 'ACTIVE' or 'INACTIVE'", required = true)

http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/UpdateController.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/UpdateController.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/UpdateController.java
index 609442b..5550358 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/UpdateController.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/UpdateController.java
@@ -21,6 +21,7 @@ import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiResponse;
 import org.apache.metron.indexing.dao.update.CommentAddRemoveRequest;
+import org.apache.metron.indexing.dao.update.Document;
 import org.apache.metron.indexing.dao.update.OriginalNotFoundException;
 import org.apache.metron.indexing.dao.update.PatchRequest;
 import org.apache.metron.indexing.dao.update.ReplaceRequest;
@@ -42,52 +43,48 @@ public class UpdateController {
   private UpdateService service;
 
   @ApiOperation(value = "Update a document with a patch")
-  @ApiResponse(message = "Nothing", code = 200)
+  @ApiResponse(message = "Returns the complete patched document.", code = 200)
   @RequestMapping(value = "/patch", method = RequestMethod.PATCH)
-  ResponseEntity<Void> patch(
+  ResponseEntity<Document> patch(
           final @ApiParam(name = "request", value = "Patch request", required = true)
                 @RequestBody
           PatchRequest request
   ) throws RestException {
     try {
-      service.patch(request);
+      return new ResponseEntity<>(service.patch(request), HttpStatus.OK);
     } catch (OriginalNotFoundException e) {
       return new ResponseEntity<>(HttpStatus.NOT_FOUND);
     }
-    return new ResponseEntity<>(HttpStatus.OK);
   }
 
   @ApiOperation(value = "Replace a document with a full replacement")
-  @ApiResponse(message = "Nothing", code = 200)
+  @ApiResponse(message = "Returns the complete replaced document.", code = 200)
   @RequestMapping(value = "/replace", method = RequestMethod.POST)
-  ResponseEntity<Void> replace(
+  ResponseEntity<Document> replace(
           final @ApiParam(name = "request", value = "Replacement request", required = true)
                 @RequestBody
           ReplaceRequest request
   ) throws RestException {
-    service.replace(request);
-    return new ResponseEntity<>(HttpStatus.OK);
+    return new ResponseEntity<>(service.replace(request), HttpStatus.OK);
   }
 
   @ApiOperation(value = "Add a comment to an alert")
-  @ApiResponse(message = "Nothing", code = 200)
+  @ApiResponse(message = "Returns the complete alert document with comments added.", code = 200)
   @RequestMapping(value = "/add/comment", method = RequestMethod.POST)
-  ResponseEntity<Void> addCommentToAlert(
+  ResponseEntity<Document> addCommentToAlert(
       @RequestBody @ApiParam(name = "request", value = "Comment add request", required = true) final
       CommentAddRemoveRequest request
   ) throws RestException {
-    service.addComment(request);
-    return new ResponseEntity<>(HttpStatus.OK);
+    return new ResponseEntity<>(service.addComment(request), HttpStatus.OK);
   }
 
-  @ApiOperation(value = "Remove a comment to an alert")
-  @ApiResponse(message = "Nothing", code = 200)
+  @ApiOperation(value = "Remove a comment from an alert")
+  @ApiResponse(message = "Returns the complete alert document with comments removed.", code = 200)
   @RequestMapping(value = "/remove/comment", method = RequestMethod.POST)
-  ResponseEntity<Void> removeCommentFromAlert(
+  ResponseEntity<Document> removeCommentFromAlert(
       @RequestBody @ApiParam(name = "request", value = "Comment remove request", required = true) final
       CommentAddRemoveRequest request
   ) throws RestException {
-    service.removeComment(request);
-    return new ResponseEntity<>(HttpStatus.OK);
+    return new ResponseEntity<>(service.removeComment(request), HttpStatus.OK);
   }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/MetaAlertService.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/MetaAlertService.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/MetaAlertService.java
index e8abaf3..4ebebb6 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/MetaAlertService.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/MetaAlertService.java
@@ -18,24 +18,22 @@
 
 package org.apache.metron.rest.service;
 
-import java.io.IOException;
-import java.util.Collection;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertAddRemoveRequest;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertCreateRequest;
-import org.apache.metron.indexing.dao.metaalert.MetaAlertCreateResponse;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertStatus;
 import org.apache.metron.indexing.dao.search.SearchResponse;
+import org.apache.metron.indexing.dao.update.Document;
 import org.apache.metron.rest.RestException;
 
 public interface MetaAlertService {
 
-  MetaAlertCreateResponse create(MetaAlertCreateRequest createRequest) throws RestException;
+  Document create(MetaAlertCreateRequest createRequest) throws RestException;
 
   SearchResponse getAllMetaAlertsForAlert(String guid) throws RestException;
 
-  boolean addAlertsToMetaAlert(MetaAlertAddRemoveRequest metaAlertAddRemoveRequest) throws RestException;
+  Document addAlertsToMetaAlert(MetaAlertAddRemoveRequest metaAlertAddRemoveRequest) throws RestException;
 
-  boolean removeAlertsFromMetaAlert(MetaAlertAddRemoveRequest metaAlertAddRemoveRequest) throws RestException;
+  Document removeAlertsFromMetaAlert(MetaAlertAddRemoveRequest metaAlertAddRemoveRequest) throws RestException;
 
-  boolean updateMetaAlertStatus(String metaAlertGuid, MetaAlertStatus status) throws RestException;
+  Document updateMetaAlertStatus(String metaAlertGuid, MetaAlertStatus status) throws RestException;
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/UpdateService.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/UpdateService.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/UpdateService.java
index bd59f39..19b3485 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/UpdateService.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/UpdateService.java
@@ -18,6 +18,7 @@
 package org.apache.metron.rest.service;
 
 import org.apache.metron.indexing.dao.update.CommentAddRemoveRequest;
+import org.apache.metron.indexing.dao.update.Document;
 import org.apache.metron.indexing.dao.update.OriginalNotFoundException;
 import org.apache.metron.indexing.dao.update.PatchRequest;
 import org.apache.metron.indexing.dao.update.ReplaceRequest;
@@ -25,8 +26,8 @@ import org.apache.metron.rest.RestException;
 
 public interface UpdateService {
 
-  void patch(PatchRequest request) throws RestException, OriginalNotFoundException;
-  void replace(ReplaceRequest request) throws RestException;
-  void addComment(CommentAddRemoveRequest request) throws RestException;
-  void removeComment(CommentAddRemoveRequest request) throws RestException;
+  Document patch(PatchRequest request) throws RestException, OriginalNotFoundException;
+  Document replace(ReplaceRequest request) throws RestException;
+  Document addComment(CommentAddRemoveRequest request) throws RestException;
+  Document removeComment(CommentAddRemoveRequest request) throws RestException;
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/MetaAlertServiceImpl.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/MetaAlertServiceImpl.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/MetaAlertServiceImpl.java
index 3f9b3e4..bd8419f 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/MetaAlertServiceImpl.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/MetaAlertServiceImpl.java
@@ -23,11 +23,11 @@ import org.apache.metron.indexing.dao.IndexDao;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertDao;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertAddRemoveRequest;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertCreateRequest;
-import org.apache.metron.indexing.dao.metaalert.MetaAlertCreateResponse;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertStatus;
 import org.apache.metron.indexing.dao.search.InvalidCreateException;
 import org.apache.metron.indexing.dao.search.InvalidSearchException;
 import org.apache.metron.indexing.dao.search.SearchResponse;
+import org.apache.metron.indexing.dao.update.Document;
 import org.apache.metron.rest.RestException;
 import org.apache.metron.rest.service.MetaAlertService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -47,7 +47,7 @@ public class MetaAlertServiceImpl implements MetaAlertService {
   }
 
   @Override
-  public MetaAlertCreateResponse create(MetaAlertCreateRequest createRequest) throws RestException {
+  public Document create(MetaAlertCreateRequest createRequest) throws RestException {
     try {
       return dao.createMetaAlert(createRequest);
     } catch (InvalidCreateException | IOException e) {
@@ -65,25 +65,25 @@ public class MetaAlertServiceImpl implements MetaAlertService {
   }
 
   @Override
-  public boolean addAlertsToMetaAlert(MetaAlertAddRemoveRequest metaAlertAddRemoveRequest) throws RestException {
+  public Document addAlertsToMetaAlert(MetaAlertAddRemoveRequest metaAlertAddRemoveRequest) throws RestException {
     try {
       return dao.addAlertsToMetaAlert(metaAlertAddRemoveRequest.getMetaAlertGuid(), metaAlertAddRemoveRequest.getAlerts());
-    } catch (IOException ioe) {
-      throw new RestException(ioe.getMessage(), ioe);
+    } catch (IOException | IllegalStateException e) {
+      throw new RestException(e.getMessage(), e);
     }
   }
 
   @Override
-  public boolean removeAlertsFromMetaAlert(MetaAlertAddRemoveRequest metaAlertAddRemoveRequest) throws RestException {
+  public Document removeAlertsFromMetaAlert(MetaAlertAddRemoveRequest metaAlertAddRemoveRequest) throws RestException {
     try {
       return dao.removeAlertsFromMetaAlert(metaAlertAddRemoveRequest.getMetaAlertGuid(), metaAlertAddRemoveRequest.getAlerts());
-    } catch (IOException ioe) {
-      throw new RestException(ioe.getMessage(), ioe);
+    } catch (IOException | IllegalStateException e) {
+      throw new RestException(e.getMessage(), e);
     }
   }
 
   @Override
-  public boolean updateMetaAlertStatus(String metaAlertGuid, MetaAlertStatus status)
+  public Document updateMetaAlertStatus(String metaAlertGuid, MetaAlertStatus status)
       throws RestException {
     try {
       return dao.updateMetaAlertStatus(metaAlertGuid, status);

http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/UpdateServiceImpl.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/UpdateServiceImpl.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/UpdateServiceImpl.java
index 49490fd..63dd4c0 100644
--- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/UpdateServiceImpl.java
+++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/service/impl/UpdateServiceImpl.java
@@ -19,6 +19,7 @@ package org.apache.metron.rest.service.impl;
 
 import org.apache.metron.indexing.dao.IndexDao;
 import org.apache.metron.indexing.dao.update.CommentAddRemoveRequest;
+import org.apache.metron.indexing.dao.update.Document;
 import org.apache.metron.indexing.dao.update.OriginalNotFoundException;
 import org.apache.metron.indexing.dao.update.PatchRequest;
 import org.apache.metron.indexing.dao.update.ReplaceRequest;
@@ -43,37 +44,36 @@ public class UpdateServiceImpl implements UpdateService {
 
 
   @Override
-  public void patch(PatchRequest request) throws RestException, OriginalNotFoundException {
+  public Document patch(PatchRequest request) throws RestException, OriginalNotFoundException {
     try {
-      dao.patch(dao, request, Optional.of(System.currentTimeMillis()));
+      return dao.patch(dao, request, Optional.of(System.currentTimeMillis()));
     } catch (Exception e) {
-
       throw new RestException(e.getMessage(), e);
     }
   }
 
   @Override
-  public void replace(ReplaceRequest request) throws RestException {
+  public Document replace(ReplaceRequest request) throws RestException {
     try {
-      dao.replace(request, Optional.of(System.currentTimeMillis()));
+      return dao.replace(request, Optional.of(System.currentTimeMillis()));
     } catch (Exception e) {
       throw new RestException(e.getMessage(), e);
     }
   }
 
   @Override
-  public void addComment(CommentAddRemoveRequest request) throws RestException {
+  public Document addComment(CommentAddRemoveRequest request) throws RestException {
     try {
-      dao.addCommentToAlert(request);
+      return dao.addCommentToAlert(request);
     } catch (Exception e) {
       throw new RestException(e.getMessage(), e);
     }
   }
 
   @Override
-  public void removeComment(CommentAddRemoveRequest request) throws RestException {
+  public Document removeComment(CommentAddRemoveRequest request) throws RestException {
     try {
-      dao.removeCommentFromAlert(request);
+      return dao.removeCommentFromAlert(request);
     } catch (Exception e) {
       throw new RestException(e.getMessage(), e);
     }

http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/MetaAlertControllerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/MetaAlertControllerIntegrationTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/MetaAlertControllerIntegrationTest.java
index b216990..8e3abe5 100644
--- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/MetaAlertControllerIntegrationTest.java
+++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/MetaAlertControllerIntegrationTest.java
@@ -19,6 +19,10 @@
 package org.apache.metron.rest.controller;
 
 import static org.apache.metron.rest.MetronRestConstants.TEST_PROFILE;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.notNullValue;
 import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
 import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
 import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
@@ -29,15 +33,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
 
 import com.google.common.collect.ImmutableMap;
 import java.util.ArrayList;
-import java.util.Arrays;
 import org.adrianwalker.multilinestring.Multiline;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.metron.common.utils.JSONUtils;
 import org.apache.metron.indexing.dao.InMemoryMetaAlertDao;
 import org.apache.metron.indexing.dao.SearchIntegrationTest;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertAddRemoveRequest;
-import org.apache.metron.indexing.dao.metaalert.MetaAlertCreateRequest;
-import org.apache.metron.indexing.dao.metaalert.MetaAlertCreateResponse;
+import org.apache.metron.indexing.dao.metaalert.MetaAlertConstants;
 import org.apache.metron.indexing.dao.search.GetRequest;
 import org.apache.metron.rest.service.MetaAlertService;
 import org.junit.After;
@@ -96,8 +98,8 @@ public class MetaAlertControllerIntegrationTest extends DaoControllerTest {
 
   /**
    * [
-   *{"guid":"meta_1","metron_alert":[{"guid":"bro_1"}],"average":"5.0","min":"5.0","median":"5.0","max":"5.0","count":"1.0","sum":"5.0"},
-   *{"guid":"meta_2","metron_alert":[{"guid":"bro_1"},{"guid":"bro_2"},{"guid":"snort_1"}],"average":"5.0","min":"0.0","median":"5.0","max":"10.0","count":"3.0","sum":"15.0"}
+   *{"guid":"meta_1","metron_alert":[{"guid":"bro_1", "source.type":"bro"}],"average":"5.0","min":"5.0","median":"5.0","max":"5.0","count":"1.0","sum":"5.0", "status":"active"},
+   *{"guid":"meta_2","metron_alert":[{"guid":"bro_1", "source.type":"bro"},{"guid":"bro_2", "source.type":"bro"},{"guid":"snort_1", "source.type":"snort"}],"average":"5.0","min":"0.0","median":"5.0","max":"10.0","count":"3.0","sum":"15.0"}
    * ]
    */
   @Multiline
@@ -163,58 +165,32 @@ public class MetaAlertControllerIntegrationTest extends DaoControllerTest {
         .andExpect(jsonPath("$.results[0].source.count").value(3.0))
         .andExpect(jsonPath("$.results[1].source.guid").value("meta_1"))
         .andExpect(jsonPath("$.results[1].source.count").value(1.0));
+  }
 
-    result = this.mockMvc.perform(
-        post(metaalertUrl + "/create")
-            .with(httpBasic(user, password)).with(csrf())
-            .contentType(MediaType.parseMediaType("application/json;charset=UTF-8"))
-            .content(create));
-    result.andExpect(status().isOk());
-
-    // Test that we can find the newly created meta alert by the sub alerts
-    guid = "bro_1";
-    result = this.mockMvc.perform(
-        post(metaalertUrl + "/searchByAlert")
-            .with(httpBasic(user, password)).with(csrf())
-            .contentType(MediaType.parseMediaType("text/plain;charset=UTF-8"))
-            .content(guid));
-    result.andExpect(status().isOk())
-        .andExpect(
-            content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
-        .andExpect(jsonPath("$.total").value(3))
-        .andExpect(jsonPath("$.results[0].source.guid").value("meta_3"))
-        .andExpect(jsonPath("$.results[0].source.count").value(2.0))
-        .andExpect(jsonPath("$.results[1].source.guid").value("meta_2"))
-        .andExpect(jsonPath("$.results[1].source.count").value(3.0))
-        .andExpect(jsonPath("$.results[2].source.guid").value("meta_1"))
-        .andExpect(jsonPath("$.results[2].source.count").value(1.0));
-
-    guid = "snort_2";
-    result = this.mockMvc.perform(
-        post(metaalertUrl + "/searchByAlert")
-            .with(httpBasic(user, password)).with(csrf())
-            .contentType(MediaType.parseMediaType("text/plain;charset=UTF-8"))
-            .content(guid));
+  @Test
+  public void shouldCreateMetaAlert() throws Exception {
+    ResultActions result = this.mockMvc.perform(
+            post(metaalertUrl + "/create")
+                    .with(httpBasic(user, password)).with(csrf())
+                    .contentType(MediaType.parseMediaType("application/json;charset=UTF-8"))
+                    .content(create));
     result.andExpect(status().isOk())
-        .andExpect(
-            content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8")))
-        .andExpect(jsonPath("$.total").value(1))
-        .andExpect(jsonPath("$.results[0].source.guid").value("meta_3"))
-        .andExpect(jsonPath("$.results[0].source.count").value(2.0));
+            .andExpect(jsonPath("$.guid", notNullValue()))
+            .andExpect(jsonPath("$.timestamp", greaterThan(0L)))
+            .andExpect(jsonPath("$.sensorType").value(MetaAlertConstants.METAALERT_TYPE))
+            .andExpect(jsonPath("$.document.timestamp", greaterThan(0L)))
+            .andExpect(jsonPath("$.document['source.type']").value(MetaAlertConstants.METAALERT_TYPE))
+            .andExpect(jsonPath("$.document.status").value("active"))
+            .andExpect(jsonPath("$.document.groups[0]").value("group_one"))
+            .andExpect(jsonPath("$.document.groups[1]").value("group_two"))
+            .andExpect(jsonPath("$.document.metron_alert[0].guid").value("bro_1"))
+            .andExpect(jsonPath("$.document.metron_alert[1].guid").value("snort_2"));
   }
 
   @Test
   public void shouldAddRemoveAlerts() throws Exception {
-    MetaAlertCreateRequest metaAlertCreateRequest = new MetaAlertCreateRequest();
-    metaAlertCreateRequest.setGroups(Arrays.asList("group_one", "group_two"));
-    metaAlertCreateRequest.setAlerts(new ArrayList<GetRequest>() {{
-      add(new GetRequest("bro_1", "bro", "bro_index_2017.01.01.01"));
-      add(new GetRequest("snort_2", "snort", "snort_index_2017.01.01.01"));
-    }});
-    MetaAlertCreateResponse metaAlertCreateResponse = metaAlertService.create(metaAlertCreateRequest);
-
     MetaAlertAddRemoveRequest addRequest = new MetaAlertAddRemoveRequest();
-    addRequest.setMetaAlertGuid(metaAlertCreateResponse.getGuid());
+    addRequest.setMetaAlertGuid("meta_1");
     addRequest.setAlerts(new ArrayList<GetRequest>() {{
       add(new GetRequest("bro_2", "bro", "bro_index_2017.01.01.01"));
       add(new GetRequest("bro_3", "bro", "bro_index_2017.01.01.01"));
@@ -225,10 +201,17 @@ public class MetaAlertControllerIntegrationTest extends DaoControllerTest {
             .with(httpBasic(user, password)).with(csrf())
             .contentType(MediaType.parseMediaType("application/json;charset=UTF-8"))
             .content(JSONUtils.INSTANCE.toJSON(addRequest, false)));
-    result.andExpect(status().isOk()).andExpect(content().string("true"));
+    result.andExpect(status().isOk())
+            .andExpect(jsonPath("$.guid").value("meta_1"))
+            .andExpect(jsonPath("$.sensorType").value(MetaAlertConstants.METAALERT_TYPE))
+            .andExpect(jsonPath("$.document.metron_alert[0].guid").value("bro_1"))
+            .andExpect(jsonPath("$.document.metron_alert[1].guid").value("bro_2"))
+            .andExpect(jsonPath("$.document.metron_alert[2].metaalerts").value("meta_1"))
+            .andExpect(jsonPath("$.document.metron_alert[2].guid").value("bro_3"))
+            .andExpect(jsonPath("$.document.metron_alert[2].metaalerts").value("meta_1"));
 
     MetaAlertAddRemoveRequest addDuplicateRequest = new MetaAlertAddRemoveRequest();
-    addDuplicateRequest.setMetaAlertGuid(metaAlertCreateResponse.getGuid());
+    addDuplicateRequest.setMetaAlertGuid("meta_1");
     addDuplicateRequest.setAlerts(new ArrayList<GetRequest>() {{
       add(new GetRequest("bro_1", "bro"));
     }});
@@ -238,10 +221,17 @@ public class MetaAlertControllerIntegrationTest extends DaoControllerTest {
             .with(httpBasic(user, password)).with(csrf())
             .contentType(MediaType.parseMediaType("application/json;charset=UTF-8"))
             .content(JSONUtils.INSTANCE.toJSON(addDuplicateRequest, false)));
-    result.andExpect(status().isOk()).andExpect(content().string("false"));
+    result.andExpect(status().isOk())
+            .andExpect(jsonPath("$.guid").value("meta_1"))
+            .andExpect(jsonPath("$.sensorType").value(MetaAlertConstants.METAALERT_TYPE))
+            .andExpect(jsonPath("$.document.metron_alert[0].guid").value("bro_1"))
+            .andExpect(jsonPath("$.document.metron_alert[1].guid").value("bro_2"))
+            .andExpect(jsonPath("$.document.metron_alert[2].metaalerts").value("meta_1"))
+            .andExpect(jsonPath("$.document.metron_alert[2].guid").value("bro_3"))
+            .andExpect(jsonPath("$.document.metron_alert[2].metaalerts").value("meta_1"));
 
     MetaAlertAddRemoveRequest removeRequest = new MetaAlertAddRemoveRequest();
-    removeRequest.setMetaAlertGuid(metaAlertCreateResponse.getGuid());
+    removeRequest.setMetaAlertGuid("meta_1");
     removeRequest.setAlerts(new ArrayList<GetRequest>() {{
       add(new GetRequest("bro_2", "bro"));
       add(new GetRequest("bro_3", "bro"));
@@ -252,12 +242,16 @@ public class MetaAlertControllerIntegrationTest extends DaoControllerTest {
             .with(httpBasic(user, password)).with(csrf())
             .contentType(MediaType.parseMediaType("application/json;charset=UTF-8"))
             .content(JSONUtils.INSTANCE.toJSON(removeRequest, false)));
-    result.andExpect(status().isOk()).andExpect(content().string("true"));
+    result.andExpect(status().isOk())
+            .andExpect(jsonPath("$.guid").value("meta_1"))
+            .andExpect(jsonPath("$.sensorType").value(MetaAlertConstants.METAALERT_TYPE))
+            .andExpect(jsonPath("$.document.metron_alert.*", hasSize(equalTo(1))))
+            .andExpect(jsonPath("$.document.metron_alert[0].guid").value("bro_1"));
 
     MetaAlertAddRemoveRequest removeMissingRequest = new MetaAlertAddRemoveRequest();
-    addRequest.setMetaAlertGuid(metaAlertCreateResponse.getGuid());
+    removeMissingRequest.setMetaAlertGuid("meta_1");
     removeMissingRequest.setAlerts(new ArrayList<GetRequest>() {{
-      add(new GetRequest("bro_1", "bro"));
+      add(new GetRequest("bro_2", "bro"));
     }});
 
     result = this.mockMvc.perform(
@@ -265,31 +259,47 @@ public class MetaAlertControllerIntegrationTest extends DaoControllerTest {
             .with(httpBasic(user, password)).with(csrf())
             .contentType(MediaType.parseMediaType("application/json;charset=UTF-8"))
             .content(JSONUtils.INSTANCE.toJSON(removeMissingRequest, false)));
-    result.andExpect(status().isOk()).andExpect(content().string("false"));
+    result.andExpect(status().isOk())
+            .andExpect(jsonPath("$.guid").value("meta_1"))
+            .andExpect(jsonPath("$.sensorType").value(MetaAlertConstants.METAALERT_TYPE))
+            .andExpect(jsonPath("$.document.metron_alert.*", hasSize(equalTo(1))))
+            .andExpect(jsonPath("$.document.metron_alert[0].guid").value("bro_1"));
+
+    MetaAlertAddRemoveRequest emptyMetaAlertRequest = new MetaAlertAddRemoveRequest();
+    emptyMetaAlertRequest.setMetaAlertGuid("meta_1");
+    emptyMetaAlertRequest.setAlerts(new ArrayList<GetRequest>() {{
+      add(new GetRequest("bro_1", "bro"));
+    }});
+
+    result = this.mockMvc.perform(
+            post(metaalertUrl + "/remove/alert")
+                    .with(httpBasic(user, password)).with(csrf())
+                    .contentType(MediaType.parseMediaType("application/json;charset=UTF-8"))
+                    .content(JSONUtils.INSTANCE.toJSON(emptyMetaAlertRequest, false)));
+    result.andExpect(status().isInternalServerError())
+            .andExpect(jsonPath("$.message").value("Removing these alerts will result in an empty meta alert.  Empty meta alerts are not allowed."))
+            .andExpect(jsonPath("$.fullMessage").value("IllegalStateException: Removing these alerts will result in an empty meta alert.  Empty meta alerts are not allowed."));
   }
 
   @Test
   public void shouldUpdateStatus() throws Exception {
-    MetaAlertCreateRequest metaAlertCreateRequest = new MetaAlertCreateRequest();
-    metaAlertCreateRequest.setGroups(Arrays.asList("group_one", "group_two"));
-    metaAlertCreateRequest.setAlerts(new ArrayList<GetRequest>() {{
-      add(new GetRequest("bro_1", "bro", "bro_index_2017.01.01.01"));
-      add(new GetRequest("snort_2", "snort", "snort_index_2017.01.01.01"));
-    }});
-
-    MetaAlertCreateResponse metaAlertCreateResponse = metaAlertService.create(metaAlertCreateRequest);
-
     ResultActions result = this.mockMvc.perform(
-        post(metaalertUrl + "/update/status/" + metaAlertCreateResponse.getGuid() + "/inactive")
+        post(metaalertUrl + "/update/status/meta_2/inactive")
             .with(httpBasic(user, password)).with(csrf())
             .contentType(MediaType.parseMediaType("application/json;charset=UTF-8")));
-    result.andExpect(status().isOk()).andExpect(content().string("true"));
+    result.andExpect(status().isOk())
+            .andExpect(jsonPath("$.guid").value("meta_2"))
+            .andExpect(jsonPath("$.sensorType").value(MetaAlertConstants.METAALERT_TYPE))
+            .andExpect(jsonPath("$.document.status").value("inactive"));
 
     result = this.mockMvc.perform(
-        post(metaalertUrl + "/update/status/" + metaAlertCreateResponse.getGuid() + "/inactive")
+        post(metaalertUrl + "/update/status/meta_2/active")
             .with(httpBasic(user, password)).with(csrf())
             .contentType(MediaType.parseMediaType("application/json;charset=UTF-8")));
-    result.andExpect(status().isOk()).andExpect(content().string("false"));
+    result.andExpect(status().isOk())
+            .andExpect(jsonPath("$.guid").value("meta_2"))
+            .andExpect(jsonPath("$.sensorType").value(MetaAlertConstants.METAALERT_TYPE))
+            .andExpect(jsonPath("$.document.status").value("active"));
   }
 
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchDao.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchDao.java b/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchDao.java
index 59f25f0..9f6e1a1 100644
--- a/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchDao.java
+++ b/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchDao.java
@@ -138,34 +138,34 @@ public class ElasticsearchDao implements IndexDao {
   }
 
   @Override
-  public void update(Document update, Optional<String> index) throws IOException {
-    updateDao.update(update, index);
+  public Document update(Document update, Optional<String> index) throws IOException {
+    return updateDao.update(update, index);
   }
 
   @Override
-  public void batchUpdate(Map<Document, Optional<String>> updates) throws IOException {
-    updateDao.batchUpdate(updates);
+  public Map<Document, Optional<String>> batchUpdate(Map<Document, Optional<String>> updates) throws IOException {
+    return updateDao.batchUpdate(updates);
   }
 
   @Override
-  public void patch(RetrieveLatestDao retrieveLatestDao, PatchRequest request, Optional<Long> timestamp)
+  public Document patch(RetrieveLatestDao retrieveLatestDao, PatchRequest request, Optional<Long> timestamp)
       throws OriginalNotFoundException, IOException {
-    updateDao.patch(retrieveLatestDao, request, timestamp);
+    return updateDao.patch(retrieveLatestDao, request, timestamp);
   }
 
   @Override
-  public void replace(ReplaceRequest request, Optional<Long> timestamp) throws IOException {
-    updateDao.replace(request, timestamp);
+  public Document replace(ReplaceRequest request, Optional<Long> timestamp) throws IOException {
+    return updateDao.replace(request, timestamp);
   }
 
   @Override
-  public void addCommentToAlert(CommentAddRemoveRequest request) throws IOException {
-    updateDao.addCommentToAlert(request);
+  public Document addCommentToAlert(CommentAddRemoveRequest request) throws IOException {
+    return updateDao.addCommentToAlert(request);
   }
 
   @Override
-  public void removeCommentFromAlert(CommentAddRemoveRequest request) throws IOException {
-    updateDao.removeCommentFromAlert(request);
+  public Document removeCommentFromAlert(CommentAddRemoveRequest request) throws IOException {
+    return updateDao.removeCommentFromAlert(request);
   }
 
   @Override
@@ -179,13 +179,13 @@ public class ElasticsearchDao implements IndexDao {
   }
 
   @Override
-  public void addCommentToAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
-    this.updateDao.addCommentToAlert(request, latest);
+  public Document addCommentToAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
+    return this.updateDao.addCommentToAlert(request, latest);
   }
 
   @Override
-  public void removeCommentFromAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
-    this.updateDao.removeCommentFromAlert(request, latest);
+  public Document removeCommentFromAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
+    return this.updateDao.removeCommentFromAlert(request, latest);
   }
 
   protected Optional<String> getIndexName(String guid, String sensorType) {

http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDao.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDao.java b/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDao.java
index 55123a5..fc0b20c 100644
--- a/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDao.java
+++ b/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDao.java
@@ -26,7 +26,6 @@ import org.apache.metron.indexing.dao.RetrieveLatestDao;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertConfig;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertConstants;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertCreateRequest;
-import org.apache.metron.indexing.dao.metaalert.MetaAlertCreateResponse;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertDao;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertStatus;
 import org.apache.metron.indexing.dao.search.FieldType;
@@ -182,25 +181,25 @@ public class ElasticsearchMetaAlertDao implements MetaAlertDao {
   }
 
   @Override
-  public MetaAlertCreateResponse createMetaAlert(MetaAlertCreateRequest request)
+  public Document createMetaAlert(MetaAlertCreateRequest request)
       throws InvalidCreateException, IOException {
     return metaAlertUpdateDao.createMetaAlert(request);
   }
 
   @Override
-  public boolean addAlertsToMetaAlert(String metaAlertGuid, List<GetRequest> alertRequests)
+  public Document addAlertsToMetaAlert(String metaAlertGuid, List<GetRequest> alertRequests)
       throws IOException {
     return metaAlertUpdateDao.addAlertsToMetaAlert(metaAlertGuid, alertRequests);
   }
 
   @Override
-  public boolean removeAlertsFromMetaAlert(String metaAlertGuid, List<GetRequest> alertRequests)
+  public Document removeAlertsFromMetaAlert(String metaAlertGuid, List<GetRequest> alertRequests)
       throws IOException {
     return metaAlertUpdateDao.removeAlertsFromMetaAlert(metaAlertGuid, alertRequests);
   }
 
   @Override
-  public boolean updateMetaAlertStatus(String metaAlertGuid, MetaAlertStatus status)
+  public Document updateMetaAlertStatus(String metaAlertGuid, MetaAlertStatus status)
       throws IOException {
     return metaAlertUpdateDao.updateMetaAlertStatus(metaAlertGuid, status);
   }
@@ -216,40 +215,40 @@ public class ElasticsearchMetaAlertDao implements MetaAlertDao {
   }
 
   @Override
-  public void update(Document update, Optional<String> index) throws IOException {
-    metaAlertUpdateDao.update(update, index);
+  public Document update(Document update, Optional<String> index) throws IOException {
+    return metaAlertUpdateDao.update(update, index);
   }
 
   @Override
-  public void batchUpdate(Map<Document, Optional<String>> updates) {
-    metaAlertUpdateDao.batchUpdate(updates);
+  public Map<Document, Optional<String>> batchUpdate(Map<Document, Optional<String>> updates) {
+    return metaAlertUpdateDao.batchUpdate(updates);
   }
 
   @Override
-  public void addCommentToAlert(CommentAddRemoveRequest request) throws IOException {
-    indexDao.addCommentToAlert(request);
+  public Document addCommentToAlert(CommentAddRemoveRequest request) throws IOException {
+    return indexDao.addCommentToAlert(request);
   }
 
   @Override
-  public void removeCommentFromAlert(CommentAddRemoveRequest request) throws IOException {
-    indexDao.removeCommentFromAlert(request);
+  public Document removeCommentFromAlert(CommentAddRemoveRequest request) throws IOException {
+    return indexDao.removeCommentFromAlert(request);
   }
 
   @Override
-  public void addCommentToAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
-    indexDao.addCommentToAlert(request, latest);
+  public Document addCommentToAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
+    return indexDao.addCommentToAlert(request, latest);
   }
 
   @Override
-  public void removeCommentFromAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
-    indexDao.removeCommentFromAlert(request, latest);
+  public Document removeCommentFromAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
+    return indexDao.removeCommentFromAlert(request, latest);
   }
 
   @Override
-  public void patch(RetrieveLatestDao retrieveLatestDao, PatchRequest request,
+  public Document patch(RetrieveLatestDao retrieveLatestDao, PatchRequest request,
       Optional<Long> timestamp)
       throws OriginalNotFoundException, IOException {
-    metaAlertUpdateDao.patch(retrieveLatestDao, request, timestamp);
+    return metaAlertUpdateDao.patch(retrieveLatestDao, request, timestamp);
   }
 
   public void setPageSize(int pageSize) {

http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertUpdateDao.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertUpdateDao.java b/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertUpdateDao.java
index bb79b7a..3b67891 100644
--- a/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertUpdateDao.java
+++ b/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertUpdateDao.java
@@ -30,7 +30,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import org.apache.lucene.search.join.ScoreMode;
 import org.apache.metron.common.Constants;
@@ -38,8 +37,6 @@ import org.apache.metron.elasticsearch.utils.ElasticsearchUtils;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertConfig;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertConstants;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertCreateRequest;
-import org.apache.metron.indexing.dao.metaalert.MetaAlertCreateResponse;
-import org.apache.metron.indexing.dao.metaalert.MetaAlertDao;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertRetrieveLatestDao;
 import org.apache.metron.indexing.dao.metaalert.MetaAlertStatus;
 import org.apache.metron.indexing.dao.metaalert.MetaScores;
@@ -82,7 +79,7 @@ public class ElasticsearchMetaAlertUpdateDao extends AbstractLuceneMetaAlertUpda
 
   @Override
   @SuppressWarnings("unchecked")
-  public MetaAlertCreateResponse createMetaAlert(MetaAlertCreateRequest request)
+  public Document createMetaAlert(MetaAlertCreateRequest request)
       throws InvalidCreateException, IOException {
     List<GetRequest> alertRequests = request.getAlerts();
     if (request.getAlerts().isEmpty()) {
@@ -134,39 +131,14 @@ public class ElasticsearchMetaAlertUpdateDao extends AbstractLuceneMetaAlertUpda
       // Kick off any updates.
       update(updates);
 
-      MetaAlertCreateResponse createResponse = new MetaAlertCreateResponse();
-      createResponse.setCreated(true);
-      createResponse.setGuid(metaAlert.getGuid());
-      return createResponse;
+      return metaAlert;
     } catch (IOException ioe) {
       throw new InvalidCreateException("Unable to create meta alert", ioe);
     }
   }
 
-  /**
-   * Adds alerts to a metaalert, based on a list of GetRequests provided for retrieval.
-   * @param metaAlertGuid The GUID of the metaalert to be given new children.
-   * @param alertRequests GetRequests for the appropriate alerts to add.
-   * @return True if metaalert is modified, false otherwise.
-   */
-  public boolean addAlertsToMetaAlert(String metaAlertGuid, List<GetRequest> alertRequests)
-      throws IOException {
-
-    Document metaAlert = retrieveLatestDao
-        .getLatest(metaAlertGuid, MetaAlertConstants.METAALERT_TYPE);
-    if (MetaAlertStatus.ACTIVE.getStatusString()
-        .equals(metaAlert.getDocument().get(MetaAlertConstants.STATUS_FIELD))) {
-      Iterable<Document> alerts = retrieveLatestDao.getAllLatest(alertRequests);
-      Map<Document, Optional<String>> updates = buildAddAlertToMetaAlertUpdates(metaAlert, alerts);
-      update(updates);
-      return updates.size() != 0;
-    } else {
-      throw new IllegalStateException("Adding alerts to an INACTIVE meta alert is not allowed");
-    }
-  }
-
   @Override
-  public void update(Document update, Optional<String> index) throws IOException {
+  public Document update(Document update, Optional<String> index) throws IOException {
     if (MetaAlertConstants.METAALERT_TYPE.equals(update.getSensorType())) {
       // We've been passed an update to the meta alert.
       throw new UnsupportedOperationException("Meta alerts cannot be directly updated");
@@ -195,29 +167,31 @@ public class ElasticsearchMetaAlertUpdateDao extends AbstractLuceneMetaAlertUpda
 
       // Run the alert's update
       elasticsearchDao.batchUpdate(updates);
+
+      return update;
     }
   }
 
   @Override
-  public void addCommentToAlert(CommentAddRemoveRequest request) throws IOException {
-    getUpdateDao().addCommentToAlert(request);
+  public Document addCommentToAlert(CommentAddRemoveRequest request) throws IOException {
+    return getUpdateDao().addCommentToAlert(request);
   }
 
   @Override
-  public void removeCommentFromAlert(CommentAddRemoveRequest request) throws IOException {
-    getUpdateDao().removeCommentFromAlert(request);
+  public Document removeCommentFromAlert(CommentAddRemoveRequest request) throws IOException {
+    return getUpdateDao().removeCommentFromAlert(request);
   }
 
   @Override
-  public void addCommentToAlert(CommentAddRemoveRequest request, Document latest)
+  public Document addCommentToAlert(CommentAddRemoveRequest request, Document latest)
       throws IOException {
-    getUpdateDao().addCommentToAlert(request, latest);
+    return getUpdateDao().addCommentToAlert(request, latest);
   }
 
   @Override
-  public void removeCommentFromAlert(CommentAddRemoveRequest request, Document latest)
+  public Document removeCommentFromAlert(CommentAddRemoveRequest request, Document latest)
       throws IOException {
-    getUpdateDao().removeCommentFromAlert(request, latest);
+    return getUpdateDao().removeCommentFromAlert(request, latest);
   }
 
   /**
@@ -243,12 +217,11 @@ public class ElasticsearchMetaAlertUpdateDao extends AbstractLuceneMetaAlertUpda
   }
 
 
-  protected boolean replaceAlertInMetaAlert(Document metaAlert, Document alert) {
+  protected void replaceAlertInMetaAlert(Document metaAlert, Document alert) {
     boolean metaAlertUpdated = removeAlertsFromMetaAlert(metaAlert,
         Collections.singleton(alert.getGuid()));
     if (metaAlertUpdated) {
       addAlertsToMetaAlert(metaAlert, Collections.singleton(alert));
     }
-    return metaAlertUpdated;
   }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchUpdateDao.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchUpdateDao.java b/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchUpdateDao.java
index f2b08d2..6843ac7 100644
--- a/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchUpdateDao.java
+++ b/metron-platform/metron-elasticsearch/src/main/java/org/apache/metron/elasticsearch/dao/ElasticsearchUpdateDao.java
@@ -60,7 +60,7 @@ public class ElasticsearchUpdateDao implements UpdateDao {
   }
 
   @Override
-  public void update(Document update, Optional<String> index) throws IOException {
+  public Document update(Document update, Optional<String> index) throws IOException {
     String indexPostfix = ElasticsearchUtils
         .getIndexFormat(accessConfig.getGlobalConfigSupplier().get()).format(new Date());
     String sensorType = update.getSensorType();
@@ -79,10 +79,11 @@ public class ElasticsearchUpdateDao implements UpdateDao {
     } catch (Exception e) {
       throw new IOException(e.getMessage(), e);
     }
+    return update;
   }
 
   @Override
-  public void batchUpdate(Map<Document, Optional<String>> updates) throws IOException {
+  public Map<Document, Optional<String>> batchUpdate(Map<Document, Optional<String>> updates) throws IOException {
     String indexPostfix = ElasticsearchUtils
         .getIndexFormat(accessConfig.getGlobalConfigSupplier().get()).format(new Date());
 
@@ -108,20 +109,22 @@ public class ElasticsearchUpdateDao implements UpdateDao {
       throw new IOException(
           "ElasticsearchDao upsert failed: " + bulkResponse.buildFailureMessage());
     }
+    return updates;
   }
 
   @Override
   @SuppressWarnings("unchecked")
-  public void addCommentToAlert(CommentAddRemoveRequest request) throws IOException {
+  public Document addCommentToAlert(CommentAddRemoveRequest request) throws IOException {
     Document latest = retrieveLatestDao.getLatest(request.getGuid(), request.getSensorType());
-    addCommentToAlert(request, latest);
+    return addCommentToAlert(request, latest);
   }
 
   @Override
   @SuppressWarnings("unchecked")
-  public void addCommentToAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
-    if (latest == null) {
-      return;
+  public Document addCommentToAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
+    if (latest == null || latest.getDocument() == null) {
+      throw new IOException(String.format("Unable to add comment. Document with guid %s cannot be found.",
+              request.getGuid()));
     }
     List<Map<String, Object>> commentsField = (List<Map<String, Object>>) latest.getDocument()
         .getOrDefault(COMMENTS_FIELD, new ArrayList<>());
@@ -133,25 +136,30 @@ public class ElasticsearchUpdateDao implements UpdateDao {
 
     Document newVersion = new Document(latest);
     newVersion.getDocument().put(COMMENTS_FIELD, originalComments);
-    update(newVersion, Optional.empty());
+    return update(newVersion, Optional.empty());
   }
 
   @Override
   @SuppressWarnings("unchecked")
-  public void removeCommentFromAlert(CommentAddRemoveRequest request) throws IOException {
+  public Document removeCommentFromAlert(CommentAddRemoveRequest request) throws IOException {
     Document latest = retrieveLatestDao.getLatest(request.getGuid(), request.getSensorType());
-    removeCommentFromAlert(request, latest);
+    return removeCommentFromAlert(request, latest);
   }
 
   @Override
   @SuppressWarnings("unchecked")
-  public void removeCommentFromAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
-    if (latest == null) {
-      return;
+  public Document removeCommentFromAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
+    if (latest == null || latest.getDocument() == null) {
+      throw new IOException(String.format("Unable to remove comment. Document with guid %s cannot be found.",
+              request.getGuid()));
     }
-    List<Map<String, Object>> commentsField = (List<Map<String, Object>>) latest.getDocument()
-        .getOrDefault(COMMENTS_FIELD, new ArrayList<>());
-    List<Map<String, Object>> originalComments = new ArrayList<>(commentsField);
+    List<Map<String, Object>> commentMap = (List<Map<String, Object>>) latest.getDocument().get(COMMENTS_FIELD);
+    // Can't remove anything if there's nothing there
+    if (commentMap == null) {
+      throw new IOException(String.format("Unable to remove comment. Document with guid %s has no comments.",
+              request.getGuid()));
+    }
+    List<Map<String, Object>> originalComments = new ArrayList<>(commentMap);
 
     List<AlertComment> alertComments = new ArrayList<>();
     for (Map<String, Object> commentRaw : originalComments) {
@@ -170,7 +178,7 @@ public class ElasticsearchUpdateDao implements UpdateDao {
       newVersion.getDocument().remove(COMMENTS_FIELD);
     }
 
-    update(newVersion, Optional.empty());
+    return update(newVersion, Optional.empty());
   }
 
   protected String getIndexName(Document update, Optional<String> index, String indexPostFix) {

http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDaoTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDaoTest.java b/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDaoTest.java
index b1da2a4..cabb992 100644
--- a/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDaoTest.java
+++ b/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/dao/ElasticsearchMetaAlertDaoTest.java
@@ -19,8 +19,6 @@
 package org.apache.metron.elasticsearch.dao;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -44,13 +42,6 @@ import org.apache.metron.indexing.dao.update.Document;
 import org.elasticsearch.index.IndexNotFoundException;
 import org.junit.Test;
 
-import java.io.IOException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -88,11 +79,13 @@ public class ElasticsearchMetaAlertDaoTest {
       }
 
       @Override
-      public void update(Document update, Optional<String> index) {
+      public Document update(Document update, Optional<String> index) {
+        return update;
       }
 
       @Override
-      public void batchUpdate(Map<Document, Optional<String>> updates) {
+      public Map<Document, Optional<String>> batchUpdate(Map<Document, Optional<String>> updates) {
+        return updates;
       }
 
       @Override
@@ -101,19 +94,23 @@ public class ElasticsearchMetaAlertDaoTest {
       }
 
       @Override
-      public void addCommentToAlert(CommentAddRemoveRequest request) {
+      public Document addCommentToAlert(CommentAddRemoveRequest request) {
+        return null;
       }
 
       @Override
-      public void removeCommentFromAlert(CommentAddRemoveRequest request) {
+      public Document removeCommentFromAlert(CommentAddRemoveRequest request) {
+        return null;
       }
 
       @Override
-      public void addCommentToAlert(CommentAddRemoveRequest request, Document latest) {
+      public Document addCommentToAlert(CommentAddRemoveRequest request, Document latest) {
+        return null;
       }
 
       @Override
-      public void removeCommentFromAlert(CommentAddRemoveRequest request, Document latest) {
+      public Document removeCommentFromAlert(CommentAddRemoveRequest request, Document latest) {
+        return null;
       }
     };
     ElasticsearchMetaAlertDao metaAlertDao = new ElasticsearchMetaAlertDao();

http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/dao/ElasticsearchUpdateDaoTest.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/dao/ElasticsearchUpdateDaoTest.java b/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/dao/ElasticsearchUpdateDaoTest.java
new file mode 100644
index 0000000..3b48a60
--- /dev/null
+++ b/metron-platform/metron-elasticsearch/src/test/java/org/apache/metron/elasticsearch/dao/ElasticsearchUpdateDaoTest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.metron.elasticsearch.dao;
+
+import org.apache.metron.indexing.dao.AccessConfig;
+import org.apache.metron.indexing.dao.UpdateDaoTest;
+import org.apache.metron.indexing.dao.update.UpdateDao;
+import org.elasticsearch.client.transport.TransportClient;
+import org.junit.Before;
+
+import static org.mockito.Mockito.mock;
+
+/**
+ * This class returns the ElasticsearchUpdateDao implementation to be used in UpdateDaoTest.  UpdateDaoTest contains a
+ * common set of tests that all Dao implementations must pass.
+ */
+public class ElasticsearchUpdateDaoTest extends UpdateDaoTest {
+
+  private TransportClient client;
+  private AccessConfig accessConfig;
+  private ElasticsearchRetrieveLatestDao retrieveLatestDao;
+  private ElasticsearchUpdateDao updateDao;
+
+  @Before
+  public void setup() {
+    client = mock(TransportClient.class);
+    accessConfig = new AccessConfig();
+    retrieveLatestDao = mock(ElasticsearchRetrieveLatestDao.class);
+    updateDao = new ElasticsearchUpdateDao(client, accessConfig, retrieveLatestDao);
+  }
+
+  @Override
+  public UpdateDao getUpdateDao() {
+    return updateDao;
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/HBaseDao.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/HBaseDao.java b/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/HBaseDao.java
index 6c646de..71d0544 100644
--- a/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/HBaseDao.java
+++ b/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/HBaseDao.java
@@ -238,13 +238,14 @@ public class HBaseDao implements IndexDao {
   }
 
   @Override
-  public synchronized void update(Document update, Optional<String> index) throws IOException {
+  public synchronized Document update(Document update, Optional<String> index) throws IOException {
     Put put = buildPut(update);
     getTableInterface().put(put);
+    return update;
   }
 
   @Override
-  public void batchUpdate(Map<Document, Optional<String>> updates) throws IOException {
+  public Map<Document, Optional<String>> batchUpdate(Map<Document, Optional<String>> updates) throws IOException {
     List<Put> puts = new ArrayList<>();
     for (Map.Entry<Document, Optional<String>> updateEntry : updates.entrySet()) {
       Document update = updateEntry.getKey();
@@ -253,6 +254,7 @@ public class HBaseDao implements IndexDao {
       puts.add(put);
     }
     getTableInterface().put(puts);
+    return updates;
   }
 
   protected Get buildGet(GetRequest getRequest) throws IOException {
@@ -280,16 +282,17 @@ public class HBaseDao implements IndexDao {
 
   @Override
   @SuppressWarnings("unchecked")
-  public void addCommentToAlert(CommentAddRemoveRequest request) throws IOException {
+  public Document addCommentToAlert(CommentAddRemoveRequest request) throws IOException {
     Document latest = getLatest(request.getGuid(), request.getSensorType());
-    addCommentToAlert(request, latest);
+    return addCommentToAlert(request, latest);
   }
 
   @Override
   @SuppressWarnings("unchecked")
-  public void addCommentToAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
+  public Document addCommentToAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
     if (latest == null || latest.getDocument() == null) {
-      throw new IOException("Unable to add comment to document that doesn't exist");
+      throw new IOException(String.format("Unable to add comment. Document with guid %s cannot be found.",
+              request.getGuid()));
     }
 
     List<Map<String, Object>> comments = (List<Map<String, Object>>) latest.getDocument()
@@ -309,28 +312,30 @@ public class HBaseDao implements IndexDao {
 
     Document newVersion = new Document(latest);
     newVersion.getDocument().put(COMMENTS_FIELD, commentsMap);
-    update(newVersion, Optional.empty());
+    return update(newVersion, Optional.empty());
   }
 
   @Override
   @SuppressWarnings("unchecked")
-  public void removeCommentFromAlert(CommentAddRemoveRequest request)
+  public Document removeCommentFromAlert(CommentAddRemoveRequest request)
       throws IOException {
     Document latest = getLatest(request.getGuid(), request.getSensorType());
-    removeCommentFromAlert(request, latest);
+    return removeCommentFromAlert(request, latest);
   }
 
   @Override
   @SuppressWarnings("unchecked")
-  public void removeCommentFromAlert(CommentAddRemoveRequest request, Document latest)
+  public Document removeCommentFromAlert(CommentAddRemoveRequest request, Document latest)
       throws IOException {
     if (latest == null || latest.getDocument() == null) {
-      throw new IOException("Unable to remove comment document that doesn't exist");
+      throw new IOException(String.format("Unable to remove comment. Document with guid %s cannot be found.",
+              request.getGuid()));
     }
     List<Map<String, Object>> commentMap = (List<Map<String, Object>>) latest.getDocument().get(COMMENTS_FIELD);
     // Can't remove anything if there's nothing there
     if (commentMap == null) {
-      return;
+      throw new IOException(String.format("Unable to remove comment. Document with guid %s has no comments.",
+              request.getGuid()));
     }
     List<Map<String, Object>> originalComments = new ArrayList<>(commentMap);
     List<AlertComment> comments = new ArrayList<>();
@@ -349,6 +354,6 @@ public class HBaseDao implements IndexDao {
       newVersion.getDocument().remove(COMMENTS_FIELD);
     }
 
-    update(newVersion, Optional.empty());
+    return update(newVersion, Optional.empty());
   }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/MultiIndexDao.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/MultiIndexDao.java b/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/MultiIndexDao.java
index 420c775..c3e2108 100644
--- a/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/MultiIndexDao.java
+++ b/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/MultiIndexDao.java
@@ -25,7 +25,6 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Optional;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -58,7 +57,7 @@ public class MultiIndexDao implements IndexDao {
   }
 
   @Override
-  public void update(final Document update, Optional<String> index) throws IOException {
+  public Document update(final Document update, Optional<String> index) throws IOException {
     List<String> exceptions =
     indices.parallelStream().map(dao -> {
       try {
@@ -71,10 +70,11 @@ public class MultiIndexDao implements IndexDao {
     if(exceptions.size() > 0) {
       throw new IOException(Joiner.on("\n").join(exceptions));
     }
+    return update;
   }
 
   @Override
-  public void batchUpdate(Map<Document, Optional<String>> updates) throws IOException {
+  public Map<Document, Optional<String>> batchUpdate(Map<Document, Optional<String>> updates) throws IOException {
     List<String> exceptions =
         indices.parallelStream().map(dao -> {
           try {
@@ -87,6 +87,7 @@ public class MultiIndexDao implements IndexDao {
     if (exceptions.size() > 0) {
       throw new IOException(Joiner.on("\n").join(exceptions));
     }
+    return updates;
   }
 
   @Override
@@ -101,51 +102,62 @@ public class MultiIndexDao implements IndexDao {
   }
 
   @Override
-  public void addCommentToAlert(CommentAddRemoveRequest request) throws IOException {
+  public Document addCommentToAlert(CommentAddRemoveRequest request) throws IOException {
     Document latest = getLatest(request.getGuid(), request.getSensorType());
-    addCommentToAlert(request, latest);
+    return addCommentToAlert(request, latest);
   }
 
-
+  /**
+   * Adds comments to an alert.  Updates are written to each Dao in parallel with the assumption that all updates
+   * are identical.  The first update to be applied is returned as the current version of the alert with comments added.
+   * @param request Request to add comments
+   * @param latest The latest version of the alert the comments will be added to.
+   * @return The complete alert document with comments added.
+   * @throws IOException
+   */
   @Override
-  public void addCommentToAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
-    List<String> exceptions =
-        indices.parallelStream().map(dao -> {
-          try {
-            dao.addCommentToAlert(request, latest);
-            return null;
-          } catch (Throwable e) {
-            return dao.getClass() + ": " + e.getMessage() + "\n" + ExceptionUtils.getStackTrace(e);
-          }
-        }).filter(Objects::nonNull).collect(Collectors.toList());
-    if (exceptions.size() > 0) {
-      throw new IOException(Joiner.on("\n").join(exceptions));
-    }
+  public Document addCommentToAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
+    List<DocumentContainer> output =
+            indices.parallelStream().map(dao -> {
+              try {
+                return new DocumentContainer(dao.addCommentToAlert(request, latest));
+              } catch (Throwable e) {
+                return new DocumentContainer(e);
+              }
+            }).collect(Collectors.toList());
+
+    return getLatestDocument(output);
   }
 
   @Override
-  public void removeCommentFromAlert(CommentAddRemoveRequest request) throws IOException {
+  public Document removeCommentFromAlert(CommentAddRemoveRequest request) throws IOException {
     Document latest = getLatest(request.getGuid(), request.getSensorType());
-    removeCommentFromAlert(request, latest);
+    return removeCommentFromAlert(request, latest);
   }
 
+  /**
+   * Removes comments from an alert.  Updates are written to each Dao in parallel with the assumption that all updates
+   * are identical.  The first update to be applied is returned as the current version of the alert with comments removed.
+   * @param request Request to remove comments
+   * @param latest The latest version of the alert the comments will be removed from.
+   * @return The complete alert document with comments removed.
+   * @throws IOException
+   */
   @Override
-  public void removeCommentFromAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
-    List<String> exceptions =
-        indices.parallelStream().map(dao -> {
-          try {
-            dao.removeCommentFromAlert(request, latest);
-            return null;
-          } catch (Throwable e) {
-            return dao.getClass() + ": " + e.getMessage() + "\n" + ExceptionUtils.getStackTrace(e);
-          }
-        }).filter(Objects::nonNull).collect(Collectors.toList());
-    if (exceptions.size() > 0) {
-      throw new IOException(Joiner.on("\n").join(exceptions));
-    }
+  public Document removeCommentFromAlert(CommentAddRemoveRequest request, Document latest) throws IOException {
+    List<DocumentContainer> output =
+            indices.parallelStream().map(dao -> {
+              try {
+                return new DocumentContainer(dao.removeCommentFromAlert(request, latest));
+              } catch (Throwable e) {
+                return new DocumentContainer(e);
+              }
+            }).collect(Collectors.toList());
+
+    return getLatestDocument(output);
   }
 
-  private static class DocumentContainer {
+  protected static class DocumentContainer {
     private Optional<Document> d = Optional.empty();
     private Optional<Throwable> t = Optional.empty();
     public DocumentContainer(Document d) {
@@ -214,7 +226,6 @@ public class MultiIndexDao implements IndexDao {
 
   @Override
   public Document getLatest(final String guid, String sensorType) throws IOException {
-    Document ret = null;
     List<DocumentContainer> output =
             indices.parallelStream().map(dao -> {
       try {
@@ -224,25 +235,7 @@ public class MultiIndexDao implements IndexDao {
       }
     }).collect(Collectors.toList());
 
-    List<String> error = new ArrayList<>();
-    for(DocumentContainer dc : output) {
-      if(dc.getException().isPresent()) {
-        Throwable e = dc.getException().get();
-        error.add(e.getMessage() + "\n" + ExceptionUtils.getStackTrace(e));
-      }
-      else {
-        if(dc.getDocument().isPresent()) {
-          Document d = dc.getDocument().get();
-          if(ret == null || ret.getTimestamp() < d.getTimestamp()) {
-            ret = d;
-          }
-        }
-      }
-    }
-    if(error.size() > 0) {
-      throw new IOException(Joiner.on("\n").join(error));
-    }
-    return ret;
+    return getLatestDocument(output);
   }
 
   @Override
@@ -282,4 +275,39 @@ public class MultiIndexDao implements IndexDao {
   public List<IndexDao> getIndices() {
     return indices;
   }
+
+  /**
+   * Returns the most recent {@link Document} from a list of {@link DocumentContainer}s.
+   *
+   * @param documentContainers A list of containers; each retrieved from a separate index.
+   * @return The latest {@link Document} found.
+   * @throws IOException If any of the {@link DocumentContainer}s contain an exception.
+   */
+  private Document getLatestDocument(List<DocumentContainer> documentContainers) throws IOException {
+    Document latestDocument = null;
+    List<String> error = new ArrayList<>();
+
+    for(DocumentContainer dc : documentContainers) {
+      if(dc.getException().isPresent()) {
+        // collect each exception; multiple can occur, one in each index
+        Throwable e = dc.getException().get();
+        error.add(e.getMessage() + "\n" + ExceptionUtils.getStackTrace(e));
+
+      } else if(dc.getDocument().isPresent()) {
+        Document d = dc.getDocument().get();
+        // is this the latest document so far?
+        if(latestDocument == null || latestDocument.getTimestamp() < d.getTimestamp()) {
+          latestDocument = d;
+        }
+
+      } else {
+        // no document was found in the index
+      }
+    }
+    if(error.size() > 0) {
+      // report all of the errors encountered
+      throw new IOException(Joiner.on("\n").join(error));
+    }
+    return latestDocument;
+  }
 }

http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/metaalert/MetaAlertCreateResponse.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/metaalert/MetaAlertCreateResponse.java b/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/metaalert/MetaAlertCreateResponse.java
deleted file mode 100644
index 0bdf332..0000000
--- a/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/metaalert/MetaAlertCreateResponse.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.metron.indexing.dao.metaalert;
-
-public class MetaAlertCreateResponse {
-  private boolean created;
-  private String guid;
-
-  public boolean isCreated() {
-    return created;
-  }
-
-  public void setCreated(boolean created) {
-    this.created = created;
-  }
-
-  public String getGuid() {
-    return guid;
-  }
-
-  public void setGuid(String guid) {
-    this.guid = guid;
-  }
-}

http://git-wip-us.apache.org/repos/asf/metron/blob/de533063/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/metaalert/MetaAlertUpdateDao.java
----------------------------------------------------------------------
diff --git a/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/metaalert/MetaAlertUpdateDao.java b/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/metaalert/MetaAlertUpdateDao.java
index f4374b4..4b22656 100644
--- a/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/metaalert/MetaAlertUpdateDao.java
+++ b/metron-platform/metron-indexing/src/main/java/org/apache/metron/indexing/dao/metaalert/MetaAlertUpdateDao.java
@@ -59,31 +59,31 @@ public interface MetaAlertUpdateDao extends UpdateDao {
    * retrieved using the DAO abstractions.
    *
    * @param request A request object containing get requests for alerts to be added and a list of groups
-   * @return A response indicating success or failure along with the GUID of the new meta alert
+   * @return The complete document of the created metaalert.
    * @throws InvalidCreateException If a malformed create request is provided
    * @throws IOException If a problem occurs during communication
    */
-  MetaAlertCreateResponse createMetaAlert(MetaAlertCreateRequest request)
+  Document createMetaAlert(MetaAlertCreateRequest request)
       throws InvalidCreateException, IOException;
 
   /**
    * Adds alerts to a metaalert, based on a list of GetRequests provided for retrieval.
    * @param metaAlertGuid The GUID of the metaalert to be given new children.
    * @param alertRequests GetRequests for the appropriate alerts to add.
-   * @return True if metaalert is modified, false otherwise.
+   * @return The complete metaalert document with the alerts added.
    */
-  boolean addAlertsToMetaAlert(String metaAlertGuid, List<GetRequest> alertRequests)
-      throws IOException;
+  Document addAlertsToMetaAlert(String metaAlertGuid, List<GetRequest> alertRequests)
+      throws IOException, IllegalStateException;
 
   /**
    * Removes alerts from a metaalert
    * @param metaAlertGuid The metaalert guid to be affected.
    * @param alertRequests A list of GetReqests that will provide the alerts to remove
-   * @return True if there are updates, false otherwise
+   * @return The complete metaalert document with the alerts removed.
    * @throws IOException If an error is thrown during retrieal.
    */
-  boolean removeAlertsFromMetaAlert(String metaAlertGuid, List<GetRequest> alertRequests)
-      throws IOException;
+  Document removeAlertsFromMetaAlert(String metaAlertGuid, List<GetRequest> alertRequests)
+      throws IOException, IllegalStateException;
 
   /**
    * Removes a metaalert link from a given alert. An nonexistent link performs no change.
@@ -115,10 +115,10 @@ public interface MetaAlertUpdateDao extends UpdateDao {
    *
    * @param metaAlertGuid The GUID of the meta alert
    * @param status A status value of 'active' or 'inactive'
-   * @return True or false depending on if the status was changed
+   * @return The complete metaalert document with the updated status.
    * @throws IOException if an error occurs during the update.
    */
-  boolean updateMetaAlertStatus(String metaAlertGuid, MetaAlertStatus status)
+  Document updateMetaAlertStatus(String metaAlertGuid, MetaAlertStatus status)
       throws IOException;
 
   /**