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();