Added a rudimentary table data controller

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

Branch: refs/heads/feature/5.x/swagger-docs
Commit: 76091fe80fc9a525df19a183a5a9047d13677510
Parents: 8d4d035
Author: Kasper Sørensen <[email protected]>
Authored: Wed Jun 8 02:58:41 2016 -0700
Committer: Kasper Sørensen <[email protected]>
Committed: Wed Jun 8 02:58:41 2016 -0700

----------------------------------------------------------------------
 .../service/app/DataContextSupplier.java        |  15 ++-
 .../service/controllers/QueryController.java    |   6 +
 .../controllers/TableDataController.java        | 109 +++++++++++++++++++
 .../TenantInteractionScenarioTest.java          |  46 +++++++-
 4 files changed, 171 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metamodel/blob/76091fe8/service-webapp/src/main/java/org/apache/metamodel/service/app/DataContextSupplier.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/app/DataContextSupplier.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/DataContextSupplier.java
index 3b36e2b..a0fb6ec 100644
--- 
a/service-webapp/src/main/java/org/apache/metamodel/service/app/DataContextSupplier.java
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/app/DataContextSupplier.java
@@ -35,14 +35,23 @@ public class DataContextSupplier implements 
Supplier<DataContext> {
 
     private final String dataSourceName;
     private final DataSourceDefinition dataSourceDefinition;
+    private final DataContext eagerLoadedDataContext;
 
     public DataContextSupplier(String dataSourceName, DataSourceDefinition 
dataSourceDefinition) {
         this.dataSourceName = dataSourceName;
         this.dataSourceDefinition = dataSourceDefinition;
+        this.eagerLoadedDataContext = createDataContext(true);
     }
 
     @Override
     public DataContext get() {
+        if (eagerLoadedDataContext != null) {
+            return eagerLoadedDataContext;
+        }
+        return createDataContext(false);
+    }
+
+    private DataContext createDataContext(boolean eager) {
         final String type = dataSourceDefinition.getType();
         switch (type.toLowerCase()) {
         case "pojo":
@@ -64,7 +73,11 @@ public class DataContextSupplier implements 
Supplier<DataContext> {
                     : dataSourceDefinition.getSchemaName();
 
             return new PojoDataContext(schemaName, tableDataProviders);
-        default:
+        }
+
+        if (eager) {
+            return null;
+        } else {
             throw new UnsupportedOperationException("Unsupported data source 
type: " + type);
         }
     }

http://git-wip-us.apache.org/repos/asf/metamodel/blob/76091fe8/service-webapp/src/main/java/org/apache/metamodel/service/controllers/QueryController.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/QueryController.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/QueryController.java
index e5c1e72..693bc4c 100644
--- 
a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/QueryController.java
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/QueryController.java
@@ -60,6 +60,12 @@ public class QueryController {
         final DataContext dataContext = 
tenantContext.getDataSourceRegistry().openDataContext(dataSourceName);
 
         final Query query = dataContext.parseQuery(queryString);
+        
+        return executeQuery(dataContext, query, offset, limit);
+    }
+
+    public static Map<String, Object> executeQuery(DataContext dataContext, 
Query query, Integer offset, Integer limit) {
+
         if (offset != null) {
             query.setFirstRow(offset);
         }

http://git-wip-us.apache.org/repos/asf/metamodel/blob/76091fe8/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TableDataController.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TableDataController.java
 
b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TableDataController.java
new file mode 100644
index 0000000..a12bbac
--- /dev/null
+++ 
b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TableDataController.java
@@ -0,0 +1,109 @@
+/**
+ * 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.metamodel.service.controllers;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.metamodel.DataContext;
+import org.apache.metamodel.UpdateSummary;
+import org.apache.metamodel.UpdateableDataContext;
+import org.apache.metamodel.insert.InsertInto;
+import org.apache.metamodel.query.Query;
+import org.apache.metamodel.schema.Schema;
+import org.apache.metamodel.schema.Table;
+import org.apache.metamodel.service.app.TenantContext;
+import org.apache.metamodel.service.app.TenantRegistry;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping(value = { 
"/{tenant}/{dataContext}/schemas/{schema}/tables/{table}/data",
+        "/{tenant}/{dataContext}/s/{schema}/t/{table}/d" }, produces = 
MediaType.APPLICATION_JSON_VALUE)
+public class TableDataController {
+
+    private final TenantRegistry tenantRegistry;
+
+    @Autowired
+    public TableDataController(TenantRegistry tenantRegistry) {
+        this.tenantRegistry = tenantRegistry;
+    }
+
+    @RequestMapping(method = RequestMethod.GET)
+    @ResponseBody
+    public Map<String, Object> get(@PathVariable("tenant") String tenantId,
+            @PathVariable("dataContext") String dataSourceName, 
@PathVariable("schema") String schemaId,
+            @PathVariable("table") String tableId, @RequestParam(value = 
"offset", required = false) Integer offset,
+            @RequestParam(value = "limit", required = false) Integer limit) {
+        final TenantContext tenantContext = 
tenantRegistry.getTenantContext(tenantId);
+        final DataContext dataContext = 
tenantContext.getDataSourceRegistry().openDataContext(dataSourceName);
+
+        final Schema schema = dataContext.getSchemaByName(schemaId);
+        final Table table = schema.getTableByName(tableId);
+
+        final Query query = 
dataContext.query().from(table).selectAll().toQuery();
+
+        return QueryController.executeQuery(dataContext, query, offset, limit);
+    }
+
+    @RequestMapping(method = RequestMethod.POST)
+    @ResponseBody
+    public Map<String, Object> post(@PathVariable("tenant") String tenantId,
+            @PathVariable("dataContext") String dataSourceName, 
@PathVariable("schema") String schemaId,
+            @PathVariable("table") String tableId, @RequestBody Map<String, 
Object> inputMap) {
+
+        final TenantContext tenantContext = 
tenantRegistry.getTenantContext(tenantId);
+        final DataContext dataContext = 
tenantContext.getDataSourceRegistry().openDataContext(dataSourceName);
+        if (!(dataContext instanceof UpdateableDataContext)) {
+            throw new UnsupportedOperationException("Cannot perform updates on 
read-only datasource: "
+                    + dataSourceName);
+        }
+
+        final Schema schema = dataContext.getSchemaByName(schemaId);
+        final Table table = schema.getTableByName(tableId);
+
+        final InsertInto insert = new InsertInto(table);
+        for (Entry<String, Object> entry : inputMap.entrySet()) {
+            insert.value(entry.getKey(), entry.getValue());
+        }
+
+        final UpdateableDataContext updateableDataContext = 
(UpdateableDataContext) dataContext;
+        final UpdateSummary result = 
updateableDataContext.executeUpdate(insert);
+
+        final Map<String, Object> response = new LinkedHashMap<>();
+        response.put("status", "ok");
+
+        if (result.getInsertedRows().isPresent()) {
+            response.put("inserted-rows", result.getInsertedRows().get());
+        }
+        if (result.getGeneratedKeys().isPresent()) {
+            response.put("generated-keys", result.getGeneratedKeys().get());
+        }
+
+        return response;
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/76091fe8/service-webapp/src/test/java/org/apache/metamodel/service/controllers/TenantInteractionScenarioTest.java
----------------------------------------------------------------------
diff --git 
a/service-webapp/src/test/java/org/apache/metamodel/service/controllers/TenantInteractionScenarioTest.java
 
b/service-webapp/src/test/java/org/apache/metamodel/service/controllers/TenantInteractionScenarioTest.java
index 35c4695..793f88c 100644
--- 
a/service-webapp/src/test/java/org/apache/metamodel/service/controllers/TenantInteractionScenarioTest.java
+++ 
b/service-webapp/src/test/java/org/apache/metamodel/service/controllers/TenantInteractionScenarioTest.java
@@ -49,9 +49,10 @@ public class TenantInteractionScenarioTest {
         TableController tableController = new TableController(tenantRegistry);
         ColumnController columnController = new 
ColumnController(tenantRegistry);
         QueryController queryController = new QueryController(tenantRegistry);
+        TableDataController tableDataController = new 
TableDataController(tenantRegistry);
 
         mockMvc = MockMvcBuilders.standaloneSetup(tenantController, 
dataContextController, schemaController,
-                tableController, columnController, queryController).build();
+                tableController, columnController, queryController, 
tableDataController).build();
     }
 
     @Test
@@ -71,7 +72,7 @@ public class TenantInteractionScenarioTest {
         // create datasource
         {
             final MvcResult result = 
mockMvc.perform(MockMvcRequestBuilders.put("/tenant1/mydata").content(
-                    "{'type':'pojo','table-definitions':'hello world (greeting 
VARCHAR, who VARCHAR); foo (bar INTEGER, baz DATE);'}"
+                    "{'type':'pojo','table-definitions':'hello_world (greeting 
VARCHAR, who VARCHAR); foo (bar INTEGER, baz DATE);'}"
                             .replace('\'', 
'"')).contentType(MediaType.APPLICATION_JSON)).andExpect(
                                     
MockMvcResultMatchers.status().isOk()).andReturn();
 
@@ -107,7 +108,7 @@ public class TenantInteractionScenarioTest {
             assertEquals("mydata", map.get("name"));
 
             assertEquals(
-                    "[{name=foo, uri=/tenant1/mydata/s/mydata/t/foo}, 
{name=hello world, uri=/tenant1/mydata/s/mydata/t/hello%20world}]",
+                    "[{name=foo, uri=/tenant1/mydata/s/mydata/t/foo}, 
{name=hello_world, uri=/tenant1/mydata/s/mydata/t/hello_world}]",
                     map.get("tables").toString());
         }
 
@@ -150,7 +151,44 @@ public class TenantInteractionScenarioTest {
             final Map<?, ?> map = new ObjectMapper().readValue(content, 
Map.class);
             assertEquals("dataset", map.get("type"));
             assertEquals("[columns.name, columns.table]", 
map.get("header").toString());
-            assertEquals("[[bar, foo], [baz, foo], [greeting, hello world], 
[who, hello world]]", map.get("data")
+            assertEquals("[[bar, foo], [baz, foo], [greeting, hello_world], 
[who, hello_world]]", map.get("data")
+                    .toString());
+        }
+
+        // insert into table (x2)
+        {
+            final MvcResult result = 
mockMvc.perform(MockMvcRequestBuilders.post(
+                    
"/tenant1/mydata/s/mydata/t/hello_world/d").content("{'greeting':'Howdy','who':'MetaModel'}"
+                            .replace('\'', 
'"')).contentType(MediaType.APPLICATION_JSON)).andExpect(
+                                    
MockMvcResultMatchers.status().isOk()).andReturn();
+
+            final String content = result.getResponse().getContentAsString();
+            final Map<?, ?> map = new ObjectMapper().readValue(content, 
Map.class);
+            assertEquals("{status=ok}", map.toString());
+        }
+        {
+            final MvcResult result = 
mockMvc.perform(MockMvcRequestBuilders.post(
+                    
"/tenant1/mydata/s/mydata/t/hello_world/d").content("{'greeting':'Hi','who':'Apache'}"
+                            .replace('\'', 
'"')).contentType(MediaType.APPLICATION_JSON)).andExpect(
+                                    
MockMvcResultMatchers.status().isOk()).andReturn();
+
+            final String content = result.getResponse().getContentAsString();
+            final Map<?, ?> map = new ObjectMapper().readValue(content, 
Map.class);
+            assertEquals("{status=ok}", map.toString());
+        }
+        
+        // query the actual data
+        // query metadata from information_schema
+        {
+            final MvcResult result = 
mockMvc.perform(MockMvcRequestBuilders.get("/tenant1/mydata/q?sql={sql}",
+                    "SELECT greeting, who AS who_is_it FROM 
hello_world")).andExpect(MockMvcResultMatchers.status()
+                            .isOk()).andReturn();
+
+            final String content = result.getResponse().getContentAsString();
+            final Map<?, ?> map = new ObjectMapper().readValue(content, 
Map.class);
+            assertEquals("dataset", map.get("type"));
+            assertEquals("[hello_world.greeting, hello_world.who AS 
who_is_it]", map.get("header").toString());
+            assertEquals("[[Howdy, MetaModel], [Hi, Apache]]", map.get("data")
                     .toString());
         }
     }

Reply via email to