Skip to content
This repository has been archived by the owner on Jun 29, 2021. It is now read-only.

Commit

Permalink
METAMODEL-1154: Added model for doing deletes and updates on table data
Browse files Browse the repository at this point in the history
  • Loading branch information
kaspersorensen committed Aug 11, 2017
1 parent 5e7dcac commit e6d3e6d
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 28 deletions.
Expand Up @@ -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();
}
}
Expand Up @@ -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_";

Expand Down
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -80,13 +95,13 @@ public QueryResponse get(@PathVariable("tenant") String tenantId,

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

Expand All @@ -95,19 +110,47 @@ public InsertionResponse post(@PathVariable("tenant") String tenantId,
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();
}
}

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();
}
insert.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));
Expand All @@ -119,4 +162,43 @@ public void run(UpdateCallback callback) {

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());
}
}
}
85 changes: 74 additions & 11 deletions core/src/main/resources/swagger.yaml
Expand Up @@ -76,7 +76,7 @@ paths:
200:
description: Tenant deleted
schema:
ref: "#/definitions/deleteTenantResponse"
$ref: "#/definitions/deleteTenantResponse"
404:
description: Tenant not found
schema:
Expand Down Expand Up @@ -271,12 +271,12 @@ paths:
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:
Expand Down Expand Up @@ -413,20 +413,83 @@ definitions:
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
Expand Down
Expand Up @@ -162,7 +162,7 @@ public void testScenario() throws Exception {
// 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();

Expand Down

0 comments on commit e6d3e6d

Please sign in to comment.