You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metamodel.apache.org by ka...@apache.org on 2016/06/14 05:20:51 UTC

[07/11] metamodel git commit: Added ability to query data and to specify POJO table defs.

Added ability to query data and to specify POJO table defs.

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

Branch: refs/heads/feature/5.x/swagger-docs
Commit: 8d4d03519d56ce482139ea42fb178504e288c892
Parents: 8722c19
Author: Kasper S�rensen <i....@gmail.com>
Authored: Wed Jun 8 02:35:20 2016 -0700
Committer: Kasper S�rensen <i....@gmail.com>
Committed: Wed Jun 8 02:39:27 2016 -0700

----------------------------------------------------------------------
 .../service/app/DataContextSupplier.java        | 72 ++++++++++++++++++
 .../service/app/DataSourceDefinition.java       |  6 ++
 .../service/app/InMemoryDataSourceRegistry.java | 24 +++---
 .../service/controllers/QueryController.java    | 79 ++++++++++++++++++++
 .../model/RestDataSourceDefinition.java         | 25 ++++++-
 .../TenantInteractionScenarioTest.java          | 66 ++++++++--------
 6 files changed, 226 insertions(+), 46 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metamodel/blob/8d4d0351/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
new file mode 100644
index 0000000..3b36e2b
--- /dev/null
+++ b/service-webapp/src/main/java/org/apache/metamodel/service/app/DataContextSupplier.java
@@ -0,0 +1,72 @@
+/**
+ * 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.app;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import org.apache.metamodel.DataContext;
+import org.apache.metamodel.pojo.ArrayTableDataProvider;
+import org.apache.metamodel.pojo.PojoDataContext;
+import org.apache.metamodel.pojo.TableDataProvider;
+import org.apache.metamodel.util.SimpleTableDef;
+import org.apache.metamodel.util.SimpleTableDefParser;
+
+public class DataContextSupplier implements Supplier<DataContext> {
+
+    private final String dataSourceName;
+    private final DataSourceDefinition dataSourceDefinition;
+
+    public DataContextSupplier(String dataSourceName, DataSourceDefinition dataSourceDefinition) {
+        this.dataSourceName = dataSourceName;
+        this.dataSourceDefinition = dataSourceDefinition;
+    }
+
+    @Override
+    public DataContext get() {
+        final String type = dataSourceDefinition.getType();
+        switch (type.toLowerCase()) {
+        case "pojo":
+            final List<TableDataProvider<?>> tableDataProviders;
+
+            final Object tableDefinitions = dataSourceDefinition.getTableDefinitions();
+            if (tableDefinitions == null) {
+                tableDataProviders = new ArrayList<>(0);
+            } else if (tableDefinitions instanceof String) {
+                final SimpleTableDef[] tableDefs = SimpleTableDefParser.parseTableDefs((String) tableDefinitions);
+                tableDataProviders = Arrays.stream(tableDefs).map((tableDef) -> {
+                    return new ArrayTableDataProvider(tableDef, new ArrayList<Object[]>());
+                }).collect(Collectors.toList());
+            } else {
+                throw new UnsupportedOperationException("Unsupported table definition type: " + tableDefinitions);
+            }
+
+            final String schemaName = dataSourceDefinition.getSchemaName() == null ? dataSourceName
+                    : dataSourceDefinition.getSchemaName();
+
+            return new PojoDataContext(schemaName, tableDataProviders);
+        default:
+            throw new UnsupportedOperationException("Unsupported data source type: " + type);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/8d4d0351/service-webapp/src/main/java/org/apache/metamodel/service/app/DataSourceDefinition.java
----------------------------------------------------------------------
diff --git a/service-webapp/src/main/java/org/apache/metamodel/service/app/DataSourceDefinition.java b/service-webapp/src/main/java/org/apache/metamodel/service/app/DataSourceDefinition.java
index 943222a..11140a7 100644
--- a/service-webapp/src/main/java/org/apache/metamodel/service/app/DataSourceDefinition.java
+++ b/service-webapp/src/main/java/org/apache/metamodel/service/app/DataSourceDefinition.java
@@ -20,5 +20,11 @@ package org.apache.metamodel.service.app;
 
 public interface DataSourceDefinition {
 
+    public String getType();
+    
+    public Object getTableDefinitions();
+    
+    public String getSchemaName();
+    
     // TODO
 }

http://git-wip-us.apache.org/repos/asf/metamodel/blob/8d4d0351/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryDataSourceRegistry.java
----------------------------------------------------------------------
diff --git a/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryDataSourceRegistry.java b/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryDataSourceRegistry.java
index f3c45e8..3a6ecee 100644
--- a/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryDataSourceRegistry.java
+++ b/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryDataSourceRegistry.java
@@ -25,7 +25,6 @@ import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
 import org.apache.metamodel.DataContext;
-import org.apache.metamodel.pojo.PojoDataContext;
 
 public class InMemoryDataSourceRegistry implements DataSourceRegistry {
 
@@ -36,19 +35,14 @@ public class InMemoryDataSourceRegistry implements DataSourceRegistry {
     }
 
     @Override
-    public String registerDataSource(final String dataContextIdentifier, final DataSourceDefinition dataContextDef)
+    public String registerDataSource(final String name, final DataSourceDefinition dataSourceDef)
             throws IllegalArgumentException {
-        if (dataSources.containsKey(dataContextIdentifier)) {
-            throw new IllegalArgumentException("DataContext already exist: " + dataContextIdentifier);
+        if (dataSources.containsKey(name)) {
+            throw new IllegalArgumentException("DataContext already exist: " + name);
         }
-        dataSources.put(dataContextIdentifier, new Supplier<DataContext>() {
-            @Override
-            public DataContext get() {
-                // TODO: Do a proper transformation from definition to instance
-                return new PojoDataContext();
-            }
-        });
-        return dataContextIdentifier;
+
+        dataSources.put(name, new DataContextSupplier(name, dataSourceDef));
+        return name;
     }
 
     @Override
@@ -57,10 +51,10 @@ public class InMemoryDataSourceRegistry implements DataSourceRegistry {
     }
 
     @Override
-    public DataContext openDataContext(String dataContextIdentifier) {
-        final Supplier<DataContext> supplier = dataSources.get(dataContextIdentifier);
+    public DataContext openDataContext(String name) {
+        final Supplier<DataContext> supplier = dataSources.get(name);
         if (supplier == null) {
-            throw new IllegalArgumentException("No such DataContext: " + dataContextIdentifier);
+            throw new IllegalArgumentException("No such DataContext: " + name);
         }
         return supplier.get();
     }

http://git-wip-us.apache.org/repos/asf/metamodel/blob/8d4d0351/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
new file mode 100644
index 0000000..e5c1e72
--- /dev/null
+++ b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/QueryController.java
@@ -0,0 +1,79 @@
+/**
+ * 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.Arrays;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.metamodel.DataContext;
+import org.apache.metamodel.data.DataSet;
+import org.apache.metamodel.query.Query;
+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.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}/query",
+        "/{tenant}/{dataContext}/q" }, produces = MediaType.APPLICATION_JSON_VALUE)
+public class QueryController {
+
+    private final TenantRegistry tenantRegistry;
+
+    @Autowired
+    public QueryController(TenantRegistry tenantRegistry) {
+        this.tenantRegistry = tenantRegistry;
+    }
+
+    @RequestMapping(method = RequestMethod.GET)
+    @ResponseBody
+    public Map<String, Object> get(@PathVariable("tenant") String tenantId,
+            @PathVariable("dataContext") String dataSourceName,
+            @RequestParam(value = "sql", required = true) String queryString,
+            @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 Query query = dataContext.parseQuery(queryString);
+        if (offset != null) {
+            query.setFirstRow(offset);
+        }
+        if (limit != null) {
+            query.setMaxRows(limit);
+        }
+
+        final DataSet dataSet = dataContext.executeQuery(query);
+
+        final Map<String, Object> map = new LinkedHashMap<>();
+        map.put("type", "dataset");
+        map.put("header", Arrays.stream(dataSet.getSelectItems()).map((si) -> si.toString()).collect(Collectors
+                .toList()));
+        map.put("data", dataSet.toObjectArrays());
+        return map;
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/8d4d0351/service-webapp/src/main/java/org/apache/metamodel/service/controllers/model/RestDataSourceDefinition.java
----------------------------------------------------------------------
diff --git a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/model/RestDataSourceDefinition.java b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/model/RestDataSourceDefinition.java
index ad5f5ba..48c5926 100644
--- a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/model/RestDataSourceDefinition.java
+++ b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/model/RestDataSourceDefinition.java
@@ -22,12 +22,35 @@ import javax.validation.constraints.NotNull;
 
 import org.apache.metamodel.service.app.DataSourceDefinition;
 
+import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
 public class RestDataSourceDefinition implements DataSourceDefinition {
 
     @JsonProperty(value = "type", required = true)
     @NotNull
-    public String type;
+    private String type;
 
+    @JsonProperty(value = "table-definitions", required = false)
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private Object tableDefinitions;
+
+    @JsonProperty(value = "schema-name", required = false)
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private String schemaName;
+
+    @Override
+    public String getType() {
+        return type;
+    }
+
+    @Override
+    public Object getTableDefinitions() {
+        return tableDefinitions;
+    }
+
+    @Override
+    public String getSchemaName() {
+        return schemaName;
+    }
 }

http://git-wip-us.apache.org/repos/asf/metamodel/blob/8d4d0351/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 acdbb15..35c4695 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
@@ -48,9 +48,10 @@ public class TenantInteractionScenarioTest {
         SchemaController schemaController = new SchemaController(tenantRegistry);
         TableController tableController = new TableController(tenantRegistry);
         ColumnController columnController = new ColumnController(tenantRegistry);
+        QueryController queryController = new QueryController(tenantRegistry);
 
         mockMvc = MockMvcBuilders.standaloneSetup(tenantController, dataContextController, schemaController,
-                tableController, columnController).build();
+                tableController, columnController, queryController).build();
     }
 
     @Test
@@ -70,15 +71,16 @@ public class TenantInteractionScenarioTest {
         // create datasource
         {
             final MvcResult result = mockMvc.perform(MockMvcRequestBuilders.put("/tenant1/mydata").content(
-                    "{'type':'pojo'}".replace('\'', '"')).contentType(MediaType.APPLICATION_JSON)).andExpect(
-                            MockMvcResultMatchers.status().isOk()).andReturn();
+                    "{'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();
 
             final String content = result.getResponse().getContentAsString();
             final Map<?, ?> map = new ObjectMapper().readValue(content, Map.class);
             assertEquals("datasource", map.get("type"));
             assertEquals("mydata", map.get("name"));
             assertEquals(
-                    "[{name=information_schema, uri=/tenant1/mydata/s/information_schema}, {name=Schema, uri=/tenant1/mydata/s/Schema}]",
+                    "[{name=information_schema, uri=/tenant1/mydata/s/information_schema}, {name=mydata, uri=/tenant1/mydata/s/mydata}]",
                     map.get("schemas").toString());
         }
 
@@ -96,56 +98,60 @@ public class TenantInteractionScenarioTest {
 
         // explore schema
         {
-            final MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/tenant1/mydata/s/information_schema"))
-                    .andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
+            final MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/tenant1/mydata/s/mydata")).andExpect(
+                    MockMvcResultMatchers.status().isOk()).andReturn();
 
             final String content = result.getResponse().getContentAsString();
             final Map<?, ?> map = new ObjectMapper().readValue(content, Map.class);
             assertEquals("schema", map.get("type"));
-            assertEquals("information_schema", map.get("name"));
+            assertEquals("mydata", map.get("name"));
 
-            assertEquals("[{name=tables, uri=/tenant1/mydata/s/information_schema/t/tables}, "
-                    + "{name=columns, uri=/tenant1/mydata/s/information_schema/t/columns}, "
-                    + "{name=relationships, uri=/tenant1/mydata/s/information_schema/t/relationships}]", map.get(
-                            "tables").toString());
+            assertEquals(
+                    "[{name=foo, uri=/tenant1/mydata/s/mydata/t/foo}, {name=hello world, uri=/tenant1/mydata/s/mydata/t/hello%20world}]",
+                    map.get("tables").toString());
         }
 
         // explore table
         {
-            final MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get(
-                    "/tenant1/mydata/s/information_schema/t/columns")).andExpect(MockMvcResultMatchers.status().isOk())
-                    .andReturn();
+            final MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/tenant1/mydata/s/mydata/t/foo"))
+                    .andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
 
             final String content = result.getResponse().getContentAsString();
             final Map<?, ?> map = new ObjectMapper().readValue(content, Map.class);
             assertEquals("table", map.get("type"));
-            assertEquals("columns", map.get("name"));
-
-            assertEquals("[{name=name, uri=/tenant1/mydata/s/information_schema/t/columns/c/name}, "
-                    + "{name=type, uri=/tenant1/mydata/s/information_schema/t/columns/c/type}, "
-                    + "{name=native_type, uri=/tenant1/mydata/s/information_schema/t/columns/c/native_type}, "
-                    + "{name=size, uri=/tenant1/mydata/s/information_schema/t/columns/c/size}, "
-                    + "{name=nullable, uri=/tenant1/mydata/s/information_schema/t/columns/c/nullable}, "
-                    + "{name=indexed, uri=/tenant1/mydata/s/information_schema/t/columns/c/indexed}, "
-                    + "{name=table, uri=/tenant1/mydata/s/information_schema/t/columns/c/table}, "
-                    + "{name=remarks, uri=/tenant1/mydata/s/information_schema/t/columns/c/remarks}]", map.get(
-                            "columns").toString());
+            assertEquals("foo", map.get("name"));
+
+            assertEquals("[{name=bar, uri=/tenant1/mydata/s/mydata/t/foo/c/bar}, "
+                    + "{name=baz, uri=/tenant1/mydata/s/mydata/t/foo/c/baz}]", map.get("columns").toString());
         }
 
         // explore column
         {
-            final MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get(
-                    "/tenant1/mydata/s/information_schema/t/columns/c/nullable")).andExpect(MockMvcResultMatchers
-                            .status().isOk()).andReturn();
+            final MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/tenant1/mydata/s/mydata/t/foo/c/bar"))
+                    .andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
 
             final String content = result.getResponse().getContentAsString();
             final Map<?, ?> map = new ObjectMapper().readValue(content, Map.class);
             assertEquals("column", map.get("type"));
-            assertEquals("nullable", map.get("name"));
+            assertEquals("bar", map.get("name"));
 
             assertEquals(
-                    "{number=4, size=null, nullable=true, primary-key=false, indexed=false, column-type=BOOLEAN, native-type=null, remarks=null}",
+                    "{number=0, size=null, nullable=true, primary-key=false, indexed=false, column-type=INTEGER, native-type=null, remarks=null}",
                     map.get("metadata").toString());
         }
+
+        // query metadata from information_schema
+        {
+            final MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/tenant1/mydata/q?sql={sql}",
+                    "SELECT name, table FROM information_schema.columns")).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("[columns.name, columns.table]", map.get("header").toString());
+            assertEquals("[[bar, foo], [baz, foo], [greeting, hello world], [who, hello world]]", map.get("data")
+                    .toString());
+        }
     }
 }