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 2017/09/08 03:54:48 UTC

metamodel-membrane git commit: METAMODEL-1158: Added handling for data sources that don't validate

Repository: metamodel-membrane
Updated Branches:
  refs/heads/master 65ab98cb4 -> 364686d8f


METAMODEL-1158: Added handling for data sources that don't validate

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

Branch: refs/heads/master
Commit: 364686d8f8ded7961789b163a6943e227f262830
Parents: 65ab98c
Author: Kasper Sørensen <i....@gmail.com>
Authored: Wed Aug 23 22:39:31 2017 -0700
Committer: Kasper Sørensen <i....@gmail.com>
Committed: Wed Aug 23 22:49:09 2017 -0700

----------------------------------------------------------------------
 .../app/CachedDataSourceRegistryWrapper.java    | 20 +++--
 .../membrane/app/DataSourceRegistry.java        | 39 +++++++-
 .../app/InMemoryDataSourceRegistry.java         |  5 ++
 .../exceptions/InvalidDataSourceException.java  | 30 +++++++
 .../file/FileBasedDataSourceRegistry.java       |  9 +-
 .../controllers/DataSourceController.java       | 55 +++++++++---
 .../membrane/controllers/RestErrorHandler.java  | 16 ++++
 .../model/RestDataSourceDefinition.java         |  4 +
 core/src/main/resources/swagger.yaml            | 14 ++-
 .../controllers/DataSourceControllerTest.java   | 93 ++++++++++++++++++++
 .../TenantInteractionScenarioTest.java          |  4 +-
 11 files changed, 259 insertions(+), 30 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/364686d8/core/src/main/java/org/apache/metamodel/membrane/app/CachedDataSourceRegistryWrapper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/CachedDataSourceRegistryWrapper.java b/core/src/main/java/org/apache/metamodel/membrane/app/CachedDataSourceRegistryWrapper.java
index 77fbb77..86aac22 100644
--- a/core/src/main/java/org/apache/metamodel/membrane/app/CachedDataSourceRegistryWrapper.java
+++ b/core/src/main/java/org/apache/metamodel/membrane/app/CachedDataSourceRegistryWrapper.java
@@ -36,14 +36,13 @@ import com.google.common.cache.RemovalNotification;
 import com.google.common.util.concurrent.UncheckedExecutionException;
 
 /**
- * A wrapper that adds a cache around a {@link DataSourceRegistry} in order to
- * prevent re-connecting all the time to the same data source.
+ * A wrapper that adds a cache around a {@link DataSourceRegistry} in order to prevent re-connecting all the time to the
+ * same data source.
  */
 public class CachedDataSourceRegistryWrapper implements DataSourceRegistry {
 
     /**
-     * The default timeout (in seconds) before the cache evicts and closes the
-     * created {@link DataContext}s.
+     * The default timeout (in seconds) before the cache evicts and closes the created {@link DataContext}s.
      */
     public static final int DEFAULT_TIMEOUT_SECONDS = 60;
 
@@ -57,8 +56,8 @@ public class CachedDataSourceRegistryWrapper implements DataSourceRegistry {
     public CachedDataSourceRegistryWrapper(final DataSourceRegistry delegate, final long cacheTimeout,
             final TimeUnit cacheTimeoutUnit) {
         this.delegate = delegate;
-        this.loadingCache = CacheBuilder.newBuilder().expireAfterAccess(cacheTimeout, cacheTimeoutUnit).removalListener(
-                createRemovalListener()).build(createCacheLoader());
+        this.loadingCache = CacheBuilder.newBuilder().expireAfterAccess(cacheTimeout, cacheTimeoutUnit)
+                .removalListener(createRemovalListener()).build(createCacheLoader());
     }
 
     private RemovalListener<String, DataContext> createRemovalListener() {
@@ -102,9 +101,14 @@ public class CachedDataSourceRegistryWrapper implements DataSourceRegistry {
             if (cause instanceof RuntimeException) {
                 throw (RuntimeException) cause;
             }
-            throw new MetaModelException("Unexpected error happened while getting DataContext '" + dataSourceName
-                    + "' from cache", e);
+            throw new MetaModelException(
+                    "Unexpected error happened while getting DataContext '" + dataSourceName + "' from cache", e);
         }
     }
 
+    @Override
+    public DataContext openDataContext(DataContextProperties properties) {
+        return delegate.openDataContext(properties);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/364686d8/core/src/main/java/org/apache/metamodel/membrane/app/DataSourceRegistry.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/DataSourceRegistry.java b/core/src/main/java/org/apache/metamodel/membrane/app/DataSourceRegistry.java
index b4678c0..fcf1ec3 100644
--- a/core/src/main/java/org/apache/metamodel/membrane/app/DataSourceRegistry.java
+++ b/core/src/main/java/org/apache/metamodel/membrane/app/DataSourceRegistry.java
@@ -34,15 +34,48 @@ public interface DataSourceRegistry {
 
     public List<String> getDataSourceNames();
 
-    public String registerDataSource(String dataSourceName, DataContextProperties dataContextProperties) throws DataSourceAlreadyExistException;
+    /**
+     * 
+     * @param dataSourceName
+     * @param dataContextProperties
+     * @return the identifier/name for the data source.
+     * @throws DataSourceAlreadyExistException
+     */
+    public String registerDataSource(String dataSourceName, DataContextProperties dataContextProperties)
+            throws DataSourceAlreadyExistException;
 
+    /**
+     * Opens a {@link DataContext} that exists in the registry.
+     * 
+     * @param dataSourceName
+     * @return
+     * @throws NoSuchDataSourceException
+     */
     public DataContext openDataContext(String dataSourceName) throws NoSuchDataSourceException;
 
-    public default UpdateableDataContext openDataContextForUpdate(String dataSourceName) {
+    /**
+     * Opens a {@link DataContext} based on a set of {@link DataContextProperties}. This allows you to instantiate a
+     * data source without necesarily having registered it (yet).
+     * 
+     * @param properties
+     * @return
+     */
+    public DataContext openDataContext(DataContextProperties properties);
+
+    /**
+     * Opens a {@link UpdateableDataContext} that exists in the registry.
+     * 
+     * @param dataSourceName
+     * @return
+     * @throws DataSourceNotUpdateableException
+     */
+    public default UpdateableDataContext openDataContextForUpdate(String dataSourceName)
+            throws DataSourceNotUpdateableException {
         final DataContext dataContext = openDataContext(dataSourceName);
         if (dataContext instanceof UpdateableDataContext) {
             return (UpdateableDataContext) dataContext;
         }
         throw new DataSourceNotUpdateableException(dataSourceName);
-    };
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/364686d8/core/src/main/java/org/apache/metamodel/membrane/app/InMemoryDataSourceRegistry.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/InMemoryDataSourceRegistry.java b/core/src/main/java/org/apache/metamodel/membrane/app/InMemoryDataSourceRegistry.java
index 1272cee..cd71ec4 100644
--- a/core/src/main/java/org/apache/metamodel/membrane/app/InMemoryDataSourceRegistry.java
+++ b/core/src/main/java/org/apache/metamodel/membrane/app/InMemoryDataSourceRegistry.java
@@ -62,4 +62,9 @@ public class InMemoryDataSourceRegistry implements DataSourceRegistry {
         return supplier.get();
     }
 
+    @Override
+    public DataContext openDataContext(DataContextProperties properties) {
+        final DataContextSupplier supplier = new DataContextSupplier(null, properties);
+        return supplier.get();
+    }
 }

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/364686d8/core/src/main/java/org/apache/metamodel/membrane/app/exceptions/InvalidDataSourceException.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/membrane/app/exceptions/InvalidDataSourceException.java b/core/src/main/java/org/apache/metamodel/membrane/app/exceptions/InvalidDataSourceException.java
new file mode 100644
index 0000000..4cdfffb
--- /dev/null
+++ b/core/src/main/java/org/apache/metamodel/membrane/app/exceptions/InvalidDataSourceException.java
@@ -0,0 +1,30 @@
+/**
+ * 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.membrane.app.exceptions;
+
+import org.apache.metamodel.MetaModelException;
+
+public class InvalidDataSourceException extends MetaModelException {
+
+    private static final long serialVersionUID = 1L;
+
+    public InvalidDataSourceException(Exception cause) {
+        super(cause.getClass().getSimpleName() + (cause.getMessage() == null ? "" : ": " + cause.getMessage()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/364686d8/core/src/main/java/org/apache/metamodel/membrane/app/registry/file/FileBasedDataSourceRegistry.java
----------------------------------------------------------------------
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 74d1d3a..7d49c6d 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
@@ -114,9 +114,14 @@ public class FileBasedDataSourceRegistry implements DataSourceRegistry {
             throw new UncheckedIOException(e);
         }
 
-        final DataContextSupplier supplier = new DataContextSupplier(dataSourceName, dataSource
-                .toDataContextProperties());
+        final DataContextSupplier supplier =
+                new DataContextSupplier(dataSourceName, dataSource.toDataContextProperties());
         return supplier.get();
     }
 
+    @Override
+    public DataContext openDataContext(DataContextProperties properties) {
+        final DataContextSupplier supplier = new DataContextSupplier(null, properties);
+        return supplier.get();
+    }
 }

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/364686d8/core/src/main/java/org/apache/metamodel/membrane/controllers/DataSourceController.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/membrane/controllers/DataSourceController.java b/core/src/main/java/org/apache/metamodel/membrane/controllers/DataSourceController.java
index 7540cf5..4f9fd55 100644
--- a/core/src/main/java/org/apache/metamodel/membrane/controllers/DataSourceController.java
+++ b/core/src/main/java/org/apache/metamodel/membrane/controllers/DataSourceController.java
@@ -30,17 +30,22 @@ import org.apache.metamodel.DataContext;
 import org.apache.metamodel.UpdateableDataContext;
 import org.apache.metamodel.factory.DataContextProperties;
 import org.apache.metamodel.factory.DataContextPropertiesImpl;
+import org.apache.metamodel.membrane.app.DataSourceRegistry;
 import org.apache.metamodel.membrane.app.TenantContext;
 import org.apache.metamodel.membrane.app.TenantRegistry;
+import org.apache.metamodel.membrane.app.exceptions.InvalidDataSourceException;
 import org.apache.metamodel.membrane.controllers.model.RestDataSourceDefinition;
 import org.apache.metamodel.membrane.swagger.model.GetDatasourceResponse;
 import org.apache.metamodel.membrane.swagger.model.GetDatasourceResponseSchemas;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 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;
 
@@ -48,6 +53,8 @@ import org.springframework.web.bind.annotation.RestController;
 @RequestMapping(value = "/{tenant}/{datasource}", produces = MediaType.APPLICATION_JSON_VALUE)
 public class DataSourceController {
 
+    private static final Logger logger = LoggerFactory.getLogger(DataSourceController.class);
+
     private final TenantRegistry tenantRegistry;
 
     @Autowired
@@ -59,16 +66,28 @@ public class DataSourceController {
     @ResponseBody
     public GetDatasourceResponse put(@PathVariable("tenant") String tenantId,
             @PathVariable("datasource") String dataSourceId,
-            @Valid @RequestBody RestDataSourceDefinition dataContextDefinition) {
+            @RequestParam(value = "validate", required = false) Boolean validate,
+            @Valid @RequestBody RestDataSourceDefinition dataSourceDefinition) {
 
         final Map<String, Object> map = new HashMap<>();
-        map.putAll(dataContextDefinition.getProperties());
-        map.put(DataContextPropertiesImpl.PROPERTY_DATA_CONTEXT_TYPE, dataContextDefinition.getType());
+        map.putAll(dataSourceDefinition.getProperties());
+        map.put(DataContextPropertiesImpl.PROPERTY_DATA_CONTEXT_TYPE, dataSourceDefinition.getType());
 
         final DataContextProperties properties = new DataContextPropertiesImpl(map);
 
-        final String dataContextIdentifier = tenantRegistry.getTenantContext(tenantId).getDataSourceRegistry()
-                .registerDataSource(dataSourceId, properties);
+        final DataSourceRegistry dataSourceRegistry = tenantRegistry.getTenantContext(tenantId).getDataSourceRegistry();
+        if (validate != null && validate.booleanValue()) {
+            // validate the data source by opening it and ensuring that a basic call such as getDefaultSchema() works.
+            try {
+                final DataContext dataContext = dataSourceRegistry.openDataContext(properties);
+                dataContext.getDefaultSchema();
+            } catch (Exception e) {
+                logger.warn("Failed validation for PUT datasource '{}/{}'", tenantId, dataSourceId, e);
+                throw new InvalidDataSourceException(e);
+            }
+        }
+
+        final String dataContextIdentifier = dataSourceRegistry.registerDataSource(dataSourceId, properties);
 
         return get(tenantId, dataContextIdentifier);
     }
@@ -78,23 +97,33 @@ public class DataSourceController {
     public GetDatasourceResponse get(@PathVariable("tenant") String tenantId,
             @PathVariable("datasource") String dataSourceName) {
         final TenantContext tenantContext = tenantRegistry.getTenantContext(tenantId);
-        final DataContext dataContext = tenantContext.getDataSourceRegistry().openDataContext(dataSourceName);
 
         final String tenantName = tenantContext.getTenantName();
         final UriBuilder uriBuilder = UriBuilder.fromPath("/{tenant}/{dataContext}/s/{schema}");
 
-        final List<GetDatasourceResponseSchemas> schemaLinks = dataContext.getSchemaNames().stream().map(s -> {
-            final String uri = uriBuilder.build(tenantName, dataSourceName, s).toString();
-            return new GetDatasourceResponseSchemas().name(s).uri(uri);
-        }).collect(Collectors.toList());
+        List<GetDatasourceResponseSchemas> schemaLinks;
+        Boolean updateable;
+        try {
+            final DataContext dataContext = tenantContext.getDataSourceRegistry().openDataContext(dataSourceName);
+            updateable = dataContext instanceof UpdateableDataContext;
+            schemaLinks = dataContext.getSchemaNames().stream().map(s -> {
+                final String uri = uriBuilder.build(tenantName, dataSourceName, s).toString();
+                return new GetDatasourceResponseSchemas().name(s).uri(uri);
+            }).collect(Collectors.toList());
+        } catch (Exception e) {
+            logger.warn("Failed to open for GET datasource '{}/{}'. No schemas will be listed.", tenantId,
+                    dataSourceName, e);
+            updateable = null;
+            schemaLinks = null;
+        }
 
         final GetDatasourceResponse resp = new GetDatasourceResponse();
         resp.type("datasource");
         resp.name(dataSourceName);
         resp.tenant(tenantName);
-        resp.updateable(dataContext instanceof UpdateableDataContext);
-        resp.queryUri(UriBuilder.fromPath("/{tenant}/{dataContext}/query").build(tenantName, dataSourceName)
-                .toString());
+        resp.updateable(updateable);
+        resp.queryUri(
+                UriBuilder.fromPath("/{tenant}/{dataContext}/query").build(tenantName, dataSourceName).toString());
         resp.schemas(schemaLinks);
         return resp;
     }

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/364686d8/core/src/main/java/org/apache/metamodel/membrane/controllers/RestErrorHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/membrane/controllers/RestErrorHandler.java b/core/src/main/java/org/apache/metamodel/membrane/controllers/RestErrorHandler.java
index 5d79f56..1b5ec4d 100644
--- a/core/src/main/java/org/apache/metamodel/membrane/controllers/RestErrorHandler.java
+++ b/core/src/main/java/org/apache/metamodel/membrane/controllers/RestErrorHandler.java
@@ -26,6 +26,7 @@ import java.util.Map;
 import org.apache.metamodel.membrane.app.exceptions.AbstractIdentifierNamingException;
 import org.apache.metamodel.membrane.app.exceptions.DataSourceAlreadyExistException;
 import org.apache.metamodel.membrane.app.exceptions.DataSourceNotUpdateableException;
+import org.apache.metamodel.membrane.app.exceptions.InvalidDataSourceException;
 import org.apache.metamodel.membrane.app.exceptions.NoSuchColumnException;
 import org.apache.metamodel.membrane.app.exceptions.NoSuchDataSourceException;
 import org.apache.metamodel.membrane.app.exceptions.NoSuchSchemaException;
@@ -129,6 +130,21 @@ public class RestErrorHandler {
         return new RestErrorResponse(HttpStatus.BAD_REQUEST.value(), "DataSource not updateable: " + ex
                 .getDataSourceName());
     }
+    
+    /**
+     * DataSource invalid exception handler method - mapped to
+     * BAD_REQUEST.
+     * 
+     * @param ex
+     * @return
+     */
+    @ExceptionHandler(InvalidDataSourceException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    @ResponseBody
+    public RestErrorResponse processDataSourceNotUpdateable(InvalidDataSourceException ex) {
+        return new RestErrorResponse(HttpStatus.BAD_REQUEST.value(), "DataSource invalid: " + ex
+                .getMessage());
+    }
 
     /**
      * Query parsing exception - mapped to BAD_REQUEST.

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/364686d8/core/src/main/java/org/apache/metamodel/membrane/controllers/model/RestDataSourceDefinition.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/membrane/controllers/model/RestDataSourceDefinition.java b/core/src/main/java/org/apache/metamodel/membrane/controllers/model/RestDataSourceDefinition.java
index eae3dc6..b3ac4ec 100644
--- a/core/src/main/java/org/apache/metamodel/membrane/controllers/model/RestDataSourceDefinition.java
+++ b/core/src/main/java/org/apache/metamodel/membrane/controllers/model/RestDataSourceDefinition.java
@@ -59,6 +59,10 @@ public class RestDataSourceDefinition implements DataSourceDefinition {
     public String getType() {
         return type;
     }
+    
+    public void setType(String type) {
+        this.type = type;
+    }
 
     @JsonAnyGetter
     @Override

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/364686d8/core/src/main/resources/swagger.yaml
----------------------------------------------------------------------
diff --git a/core/src/main/resources/swagger.yaml b/core/src/main/resources/swagger.yaml
index 1423ee7..fa17ebf 100644
--- a/core/src/main/resources/swagger.yaml
+++ b/core/src/main/resources/swagger.yaml
@@ -105,17 +105,27 @@ paths:
             $ref: "#/definitions/error"
     put:
       parameters:
+        - name: validate
+          in: query
+          description: Whether or not to validate the datasource properties and reject creating the datasource if it cannot be created.
+          required: false
+          type: boolean
+          default: false
         - name: inputData
           in: body
           description: The definition of the datasource using properties. The same properties as normally applied in MetaModel factories (e.g. 'type', 'resource', 'url', 'driver-class' ,'hostname', 'port', 'catalog', 'database', 'username', 'port', 'table-defs') are used here.
           required: true
           schema:
-            $ref: "#/definitions/putDatasourceResponse"
+            $ref: "#/definitions/putDatasourceRequest"
       responses:
         200:
           description: Datasource created
           schema:
             $ref: "#/definitions/getDatasourceResponse"
+        400:
+          description: Datasource validation failed
+          schema:
+            $ref: "#/definitions/error"
   /{tenant}/{datasource}/q:
     parameters:
       - name: tenant
@@ -499,7 +509,7 @@ definitions:
         description: An array of generated keys for the records, if any
         items:
           type: object
-  putDatasourceResponse:
+  putDatasourceRequest:
     type: object
     properties:
       type:

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/364686d8/core/src/test/java/org/apache/metamodel/membrane/controllers/DataSourceControllerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/metamodel/membrane/controllers/DataSourceControllerTest.java b/core/src/test/java/org/apache/metamodel/membrane/controllers/DataSourceControllerTest.java
new file mode 100644
index 0000000..2a7e240
--- /dev/null
+++ b/core/src/test/java/org/apache/metamodel/membrane/controllers/DataSourceControllerTest.java
@@ -0,0 +1,93 @@
+/**
+ * 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.membrane.controllers;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.metamodel.membrane.app.InMemoryTenantRegistry;
+import org.apache.metamodel.membrane.app.TenantRegistry;
+import org.apache.metamodel.membrane.app.exceptions.InvalidDataSourceException;
+import org.apache.metamodel.membrane.controllers.model.RestDataSourceDefinition;
+import org.apache.metamodel.membrane.swagger.model.GetDatasourceResponse;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class DataSourceControllerTest {
+
+    private final TenantRegistry tenantRegistry = new InMemoryTenantRegistry();
+    private final DataSourceController dataSourceController = new DataSourceController(tenantRegistry);
+
+    @Rule
+    public TestName name = new TestName();
+
+    @Test
+    public void testPutWithoutValidation() throws Exception {
+        final String tenant = name.getMethodName();
+        tenantRegistry.createTenantContext(tenant);
+
+        final RestDataSourceDefinition dataSourceDefinition = new RestDataSourceDefinition();
+        dataSourceDefinition.setType("foo bar");
+        dataSourceDefinition.set("hello", "world");
+
+        final GetDatasourceResponse resp = dataSourceController.put(tenant, "ds1", false, dataSourceDefinition);
+        assertEquals("ds1", resp.getName());
+        assertEquals(null, resp.getSchemas());
+        assertEquals(null, resp.getUpdateable());
+    }
+
+    @Test
+    public void testPutWithValidationFailing() throws Exception {
+        final String tenant = name.getMethodName();
+        tenantRegistry.createTenantContext(tenant);
+
+        final RestDataSourceDefinition dataSourceDefinition = new RestDataSourceDefinition();
+        dataSourceDefinition.setType("foo bar");
+        dataSourceDefinition.set("hello", "world");
+
+        try {
+            dataSourceController.put(tenant, "ds1", true, dataSourceDefinition);
+            Assert.fail("exception expected");
+        } catch (InvalidDataSourceException e) {
+            assertEquals("UnsupportedDataContextPropertiesException", e.getMessage());
+        }
+    }
+
+    @Test
+    public void testPutWithValidationPassing() throws Exception {
+        final String tenant = name.getMethodName();
+        tenantRegistry.createTenantContext(tenant);
+
+        final RestDataSourceDefinition dataSourceDefinition = new RestDataSourceDefinition();
+        dataSourceDefinition.setType("pojo");
+
+        final GetDatasourceResponse resp = dataSourceController.put(tenant, "ds1", true, dataSourceDefinition);
+
+        final ObjectMapper objectMapper = new ObjectMapper();
+
+        assertEquals(
+                "[{'name':'information_schema','uri':'/testPutWithValidationPassing/ds1/s/information_schema'},"
+                        + "{'name':'Schema','uri':'/testPutWithValidationPassing/ds1/s/Schema'}]",
+                objectMapper.writeValueAsString(resp.getSchemas()).replace('\"', '\''));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/metamodel-membrane/blob/364686d8/core/src/test/java/org/apache/metamodel/membrane/controllers/TenantInteractionScenarioTest.java
----------------------------------------------------------------------
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 8a56934..3e0575d 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
@@ -48,14 +48,14 @@ public class TenantInteractionScenarioTest {
 
         final TenantRegistry tenantRegistry = new InMemoryTenantRegistry();
         final TenantController tenantController = new TenantController(tenantRegistry);
-        final DataSourceController dataContextController = new DataSourceController(tenantRegistry);
+        final DataSourceController dataSourceController = new DataSourceController(tenantRegistry);
         final SchemaController schemaController = new SchemaController(tenantRegistry);
         final TableController tableController = new TableController(tenantRegistry);
         final ColumnController columnController = new ColumnController(tenantRegistry);
         final QueryController queryController = new QueryController(tenantRegistry);
         final TableDataController tableDataController = new TableDataController(tenantRegistry);
 
-        mockMvc = MockMvcBuilders.standaloneSetup(tenantController, dataContextController, schemaController,
+        mockMvc = MockMvcBuilders.standaloneSetup(tenantController, dataSourceController, schemaController,
                 tableController, columnController, queryController, tableDataController).setMessageConverters(
                         messageConverter).build();
     }