METAMODEL-1154: Added model for doing deletes and updates on table data
diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/config/JacksonConfig.java b/core/src/main/java/org/apache/metamodel/membrane/app/config/JacksonConfig.java
index a9ad0cd..d1c90da 100644
--- a/core/src/main/java/org/apache/metamodel/membrane/app/config/JacksonConfig.java
+++ b/core/src/main/java/org/apache/metamodel/membrane/app/config/JacksonConfig.java
@@ -27,10 +27,15 @@
 @Configuration
 public class JacksonConfig {
 
+    // use the JSON class from swagger-codegen
+    private static final ObjectMapper OBJECT_MAPPER = new JSON().getContext(Object.class);;
+
+    public static ObjectMapper getObjectMapper() {
+        return OBJECT_MAPPER;
+    }
+
     @Bean(name = "objectMapper")
     public ObjectMapper objectMapper() {
-        // use the JSON class from swagger-codegen
-        final JSON json = new JSON();
-        return json.getContext(Object.class);
+        return getObjectMapper();
     }
 }
diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedDataSourceRegistry.java b/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedDataSourceRegistry.java
index 6659292..74d1d3a 100644
--- a/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedDataSourceRegistry.java
+++ b/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedDataSourceRegistry.java
@@ -30,17 +30,17 @@
 import org.apache.metamodel.factory.DataContextProperties;
 import org.apache.metamodel.membrane.app.DataContextSupplier;
 import org.apache.metamodel.membrane.app.DataSourceRegistry;
+import org.apache.metamodel.membrane.app.config.JacksonConfig;
 import org.apache.metamodel.membrane.app.exceptions.DataSourceAlreadyExistException;
 import org.apache.metamodel.membrane.app.exceptions.NoSuchDataSourceException;
 import org.apache.metamodel.membrane.controllers.model.RestDataSourceDefinition;
-import org.apache.metamodel.membrane.swagger.invoker.JSON;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.base.Strings;
 
 public class FileBasedDataSourceRegistry implements DataSourceRegistry {
 
-    private static final ObjectMapper OBJECT_MAPPER = new JSON().getContext(Object.class);
+    private static final ObjectMapper OBJECT_MAPPER = JacksonConfig.getObjectMapper();
     private static final String DATASOURCE_FILE_SUFFIX = ".json";
     private static final String DATASOURCE_FILE_PREFIX = "ds_";
 
diff --git a/core/src/main/java/org/apache/metamodel/membrane/controllers/TableDataController.java b/core/src/main/java/org/apache/metamodel/membrane/controllers/TableDataController.java
index 91d2588..afbc37c 100644
--- a/core/src/main/java/org/apache/metamodel/membrane/controllers/TableDataController.java
+++ b/core/src/main/java/org/apache/metamodel/membrane/controllers/TableDataController.java
@@ -28,14 +28,28 @@
 import org.apache.metamodel.UpdateScript;
 import org.apache.metamodel.UpdateSummary;
 import org.apache.metamodel.UpdateableDataContext;
+import org.apache.metamodel.data.RowBuilder;
+import org.apache.metamodel.data.WhereClauseBuilder;
+import org.apache.metamodel.delete.RowDeletionBuilder;
 import org.apache.metamodel.insert.RowInsertionBuilder;
 import org.apache.metamodel.membrane.app.DataContextTraverser;
 import org.apache.metamodel.membrane.app.TenantContext;
 import org.apache.metamodel.membrane.app.TenantRegistry;
-import org.apache.metamodel.membrane.swagger.model.InsertionResponse;
+import org.apache.metamodel.membrane.app.config.JacksonConfig;
+import org.apache.metamodel.membrane.swagger.model.Operator;
+import org.apache.metamodel.membrane.swagger.model.PostDataRequest;
+import org.apache.metamodel.membrane.swagger.model.PostDataRequestDelete;
+import org.apache.metamodel.membrane.swagger.model.PostDataRequestUpdate;
+import org.apache.metamodel.membrane.swagger.model.PostDataResponse;
 import org.apache.metamodel.membrane.swagger.model.QueryResponse;
+import org.apache.metamodel.membrane.swagger.model.WhereCondition;
+import org.apache.metamodel.query.FilterItem;
+import org.apache.metamodel.query.OperatorType;
 import org.apache.metamodel.query.Query;
+import org.apache.metamodel.query.SelectItem;
+import org.apache.metamodel.schema.Column;
 import org.apache.metamodel.schema.Table;
+import org.apache.metamodel.update.RowUpdationBuilder;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -46,6 +60,7 @@
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.bind.annotation.RestController;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.collect.Lists;
 
 @RestController
@@ -80,13 +95,13 @@
 
     @RequestMapping(method = RequestMethod.POST)
     @ResponseBody
-    public InsertionResponse post(@PathVariable("tenant") String tenantId,
+    public PostDataResponse post(@PathVariable("tenant") String tenantId,
             @PathVariable("dataContext") String dataSourceName, @PathVariable("schema") String schemaId,
-            @PathVariable("table") String tableId, @RequestBody final List<Map<String, Object>> inputRecords) {
+            @PathVariable("table") String tableId, @RequestBody PostDataRequest postDataReq) {
 
         final TenantContext tenantContext = tenantRegistry.getTenantContext(tenantId);
-        final UpdateableDataContext dataContext = tenantContext.getDataSourceRegistry().openDataContextForUpdate(
-                dataSourceName);
+        final UpdateableDataContext dataContext =
+                tenantContext.getDataSourceRegistry().openDataContextForUpdate(dataSourceName);
 
         final DataContextTraverser traverser = new DataContextTraverser(dataContext);
 
@@ -95,19 +110,47 @@
         final UpdateSummary result = dataContext.executeUpdate(new UpdateScript() {
             @Override
             public void run(UpdateCallback callback) {
-                for (Map<String, Object> inputMap : inputRecords) {
-                    final RowInsertionBuilder insert = callback.insertInto(table);
-                    for (Entry<String, Object> entry : inputMap.entrySet()) {
-                        insert.value(entry.getKey(), entry.getValue());
+                final List<PostDataRequestUpdate> updateItems = postDataReq.getUpdate();
+                if (updateItems != null) {
+                    for (PostDataRequestUpdate updateItem : updateItems) {
+                        final RowUpdationBuilder updateBuilder = callback.update(table);
+                        setWhere(updateBuilder, table, updateItem.getWhere());
+                        setValues(updateBuilder, updateItem.getValues());
+                        updateBuilder.execute();
                     }
-                    insert.execute();
+                }
+
+                final List<PostDataRequestDelete> deleteItems = postDataReq.getDelete();
+                if (deleteItems != null) {
+                    for (PostDataRequestDelete deleteItem : deleteItems) {
+                        final RowDeletionBuilder deleteBuilder = callback.deleteFrom(table);
+                        setWhere(deleteBuilder, table, deleteItem.getWhere());
+                        deleteBuilder.execute();
+                    }
+                }
+
+                final List<Object> insertItems = postDataReq.getInsert();
+                if (insertItems != null) {
+                    for (Object insertItem : insertItems) {
+                        final RowInsertionBuilder insertBuild = callback.insertInto(table);
+                        setValues(insertBuild, insertItem);
+                        insertBuild.execute();
+                    }
                 }
             }
         });
 
-        final InsertionResponse response = new InsertionResponse();
+        final PostDataResponse response = new PostDataResponse();
         response.status("ok");
 
+        if (result.getDeletedRows().isPresent()) {
+            final Integer deletedRecords = result.getDeletedRows().get();
+            response.deletedRows(new BigDecimal(deletedRecords));
+        }
+        if (result.getUpdatedRows().isPresent()) {
+            final Integer updatedRecords = result.getUpdatedRows().get();
+            response.updatedRows(new BigDecimal(updatedRecords));
+        }
         if (result.getInsertedRows().isPresent()) {
             final Integer insertedRecords = result.getInsertedRows().get();
             response.insertedRows(new BigDecimal(insertedRecords));
@@ -119,4 +162,43 @@
 
         return response;
     }
+
+    private void setWhere(WhereClauseBuilder<?> whereBuilder, Table table, List<WhereCondition> conditions) {
+        for (WhereCondition condition : conditions) {
+            final Column column = table.getColumnByName(condition.getColumn());
+            if (column == null) {
+                throw new IllegalArgumentException("No such column: " + condition.getColumn());
+            }
+            final OperatorType operator = toOperator(condition.getOperator());
+            final FilterItem filterItem = new FilterItem(new SelectItem(column), operator, condition.getOperand());
+            whereBuilder.where(filterItem);
+        }
+    }
+
+    private OperatorType toOperator(Operator operator) {
+        switch (operator) {
+        case EQ:
+            return OperatorType.EQUALS_TO;
+        case NE:
+            return OperatorType.DIFFERENT_FROM;
+        case GT:
+            return OperatorType.GREATER_THAN;
+        case LT:
+            return OperatorType.LESS_THAN;
+        case LIKE:
+            return OperatorType.LIKE;
+        case NOT_LIKE:
+            return OperatorType.NOT_LIKE;
+        }
+        throw new UnsupportedOperationException("Unsupported operator: " + operator);
+    }
+
+    protected void setValues(RowBuilder<?> rowBuilder, Object values) {
+        final ObjectMapper objectMapper = JacksonConfig.getObjectMapper();
+        @SuppressWarnings("unchecked") final Map<String, ?> inputMap = objectMapper.convertValue(values, Map.class);
+
+        for (Entry<String, ?> entry : inputMap.entrySet()) {
+            rowBuilder.value(entry.getKey(), entry.getValue());
+        }
+    }
 }
diff --git a/core/src/main/resources/swagger.yaml b/core/src/main/resources/swagger.yaml
index f183feb..1423ee7 100644
--- a/core/src/main/resources/swagger.yaml
+++ b/core/src/main/resources/swagger.yaml
@@ -76,7 +76,7 @@
         200:
           description: Tenant deleted
           schema:
-            ref: "#/definitions/deleteTenantResponse"
+            $ref: "#/definitions/deleteTenantResponse"
         404:
           description: Tenant not found
           schema:
@@ -271,12 +271,12 @@
           description: The data to insert
           required: true
           schema:
-            $ref: "#/definitions/insertionRequest"
+            $ref: "#/definitions/postDataRequest"
       responses:
         200:
           description: Data inserted
           schema:
-            $ref: "#/definitions/insertionResponse"
+            $ref: "#/definitions/postDataResponse"
         404:
           description: Table not found
           schema:
@@ -413,20 +413,83 @@
       deleted:
         type: boolean
         description: A confirmation boolean to indicate that the deletion is effectuated.
-  insertionRequest:
-    type: array
-    items:
-      description: A record to insert where each key is expected to match a column name and each value is the value to put.
-      type: object
-  insertionResponse:
-    description: Represents the result of inserting records to a table
+  postDataRequest:
+    type: object
+    properties:
+      update:
+        type: array
+        items:
+          type: object
+          properties:
+            values:
+              type: object
+              description: Record values to update where each key is expected to match a column name and each value is the value to put.
+              example:
+                favorite_java_library: Apache MetaModel
+            where:
+              type: array
+              items:
+                $ref: "#/definitions/whereCondition"
+      delete:
+        type: array
+        items:
+          type: object
+          properties:
+            where:
+              type: array
+              items:
+                $ref: "#/definitions/whereCondition"
+      insert:
+        type: array
+        items:
+          description: A record to insert where each key is expected to match a column name and each value is the value to put.
+          type: object
+        example:
+        - fullname: Jane Doe
+          email_address: janedoe@gmail.com
+          favourite_java_library: null
+        - fullname: John Doe
+          email_address: johndoe@gmail.com
+          favourite_java_library: "MetaModel"
+  whereCondition:
+    type: object
+    properties:
+      column:
+        type: string
+      operator:
+        $ref: "#/definitions/operator"
+      operand:
+        type: object
+    example:
+      column: fullname
+      operator: like
+      operand: "John%Doe"
+  operator:
+    type: string
+    enum:
+    - "eq"
+    - "ne"
+    - "gt"
+    - "lt"
+    - "like"
+    - "not_like"
+  postDataResponse:
+    description: Represents the result of posting a data update to the records to a table
     type: object
     required:
       - status
     properties:
       status:
         type: string
-        description: A confirmation 'ok' that the insertion went well.
+        description: A confirmation 'ok' that the data updates went well.
+      updated-rows:
+        type: number
+        format: int32
+        description: The amount of updated records
+      deleted-rows:
+        type: number
+        format: int32
+        description: The amount of deleted records
       inserted-rows:
         type: number
         format: int32
diff --git a/core/src/test/java/org/apache/metamodel/membrane/controllers/TenantInteractionScenarioTest.java b/core/src/test/java/org/apache/metamodel/membrane/controllers/TenantInteractionScenarioTest.java
index eccff2c..b410e01 100644
--- a/core/src/test/java/org/apache/metamodel/membrane/controllers/TenantInteractionScenarioTest.java
+++ b/core/src/test/java/org/apache/metamodel/membrane/controllers/TenantInteractionScenarioTest.java
@@ -162,7 +162,7 @@
         // insert into table (x2)
         {
             final MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post(
-                    "/tenant1/mydata/s/mydata/t/hello_world/d").content("[{'greeting':'Howdy','who':'MetaModel'}]"
+                    "/tenant1/mydata/s/mydata/t/hello_world/d").content("{'insert':[{'greeting':'Howdy','who':'MetaModel'}]}"
                             .replace('\'', '"')).contentType(MediaType.APPLICATION_JSON)).andExpect(
                                     MockMvcResultMatchers.status().isOk()).andReturn();
 
@@ -172,7 +172,7 @@
         }
         {
             final MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post(
-                    "/tenant1/mydata/s/mydata/t/hello_world/d").content("[{'greeting':'Hi','who':'Apache'}]".replace(
+                    "/tenant1/mydata/s/mydata/t/hello_world/d").content("{'insert':[{'greeting':'Hi','who':'Apache'}]}".replace(
                             '\'', '"')).contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers
                                     .status().isOk()).andReturn();