Built out an extensive unittest to document a full usage scenario. Project: http://git-wip-us.apache.org/repos/asf/metamodel/repo Commit: http://git-wip-us.apache.org/repos/asf/metamodel/commit/8722c196 Tree: http://git-wip-us.apache.org/repos/asf/metamodel/tree/8722c196 Diff: http://git-wip-us.apache.org/repos/asf/metamodel/diff/8722c196
Branch: refs/heads/feature/5.x/swagger-docs Commit: 8722c196673713501edbc232ffe5ed3a213fbc5a Parents: 6f6f776 Author: Kasper Sørensen <[email protected]> Authored: Mon Jun 6 20:55:15 2016 -0700 Committer: Kasper Sørensen <[email protected]> Committed: Mon Jun 6 20:55:15 2016 -0700 ---------------------------------------------------------------------- .../service/app/DataContextDefinition.java | 24 --- .../service/app/DataContextRegistry.java | 35 ----- .../service/app/DataSourceDefinition.java | 24 +++ .../service/app/DataSourceRegistry.java | 35 +++++ .../app/InMemoryDataContextRegistry.java | 68 --------- .../service/app/InMemoryDataSourceRegistry.java | 68 +++++++++ .../service/app/InMemoryTenantContext.java | 8 +- .../metamodel/service/app/TenantContext.java | 4 +- .../service/controllers/ColumnController.java | 87 +++++++++++ .../controllers/DataContextController.java | 87 ----------- .../controllers/DataSourceController.java | 87 +++++++++++ .../service/controllers/SchemaController.java | 16 +- .../service/controllers/TableController.java | 20 +-- .../service/controllers/TenantController.java | 16 +- .../model/RestDataContextDefinition.java | 33 ---- .../model/RestDataSourceDefinition.java | 33 ++++ .../TenantInteractionScenarioTest.java | 151 +++++++++++++++++++ 17 files changed, 517 insertions(+), 279 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/metamodel/blob/8722c196/service-webapp/src/main/java/org/apache/metamodel/service/app/DataContextDefinition.java ---------------------------------------------------------------------- diff --git a/service-webapp/src/main/java/org/apache/metamodel/service/app/DataContextDefinition.java b/service-webapp/src/main/java/org/apache/metamodel/service/app/DataContextDefinition.java deleted file mode 100644 index 955ff6c..0000000 --- a/service-webapp/src/main/java/org/apache/metamodel/service/app/DataContextDefinition.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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; - -public interface DataContextDefinition { - - // TODO -} http://git-wip-us.apache.org/repos/asf/metamodel/blob/8722c196/service-webapp/src/main/java/org/apache/metamodel/service/app/DataContextRegistry.java ---------------------------------------------------------------------- diff --git a/service-webapp/src/main/java/org/apache/metamodel/service/app/DataContextRegistry.java b/service-webapp/src/main/java/org/apache/metamodel/service/app/DataContextRegistry.java deleted file mode 100644 index d2a7526..0000000 --- a/service-webapp/src/main/java/org/apache/metamodel/service/app/DataContextRegistry.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * 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.List; - -import org.apache.metamodel.DataContext; - -/** - * Represents a user's/tenant's registry of {@link DataContext}s. - */ -public interface DataContextRegistry { - - public List<String> getDataContextIdentifiers(); - - public String registerDataContext(String dataContextIdentifier, DataContextDefinition dataContextDef) throws IllegalArgumentException; - - public DataContext openDataContext(String dataContextIdentifier) throws IllegalArgumentException; -} http://git-wip-us.apache.org/repos/asf/metamodel/blob/8722c196/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 new file mode 100644 index 0000000..943222a --- /dev/null +++ b/service-webapp/src/main/java/org/apache/metamodel/service/app/DataSourceDefinition.java @@ -0,0 +1,24 @@ +/** + * 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; + +public interface DataSourceDefinition { + + // TODO +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/8722c196/service-webapp/src/main/java/org/apache/metamodel/service/app/DataSourceRegistry.java ---------------------------------------------------------------------- diff --git a/service-webapp/src/main/java/org/apache/metamodel/service/app/DataSourceRegistry.java b/service-webapp/src/main/java/org/apache/metamodel/service/app/DataSourceRegistry.java new file mode 100644 index 0000000..1930354 --- /dev/null +++ b/service-webapp/src/main/java/org/apache/metamodel/service/app/DataSourceRegistry.java @@ -0,0 +1,35 @@ +/** + * 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.List; + +import org.apache.metamodel.DataContext; + +/** + * Represents a user's/tenant's registry of {@link DataContext}s. + */ +public interface DataSourceRegistry { + + public List<String> getDataSourceNames(); + + public String registerDataSource(String dataContextName, DataSourceDefinition dataSourceDef) throws IllegalArgumentException; + + public DataContext openDataContext(String dataSourceName) throws IllegalArgumentException; +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/8722c196/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryDataContextRegistry.java ---------------------------------------------------------------------- diff --git a/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryDataContextRegistry.java b/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryDataContextRegistry.java deleted file mode 100644 index f9b6aa0..0000000 --- a/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryDataContextRegistry.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * 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.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import org.apache.metamodel.DataContext; -import org.apache.metamodel.pojo.PojoDataContext; - -public class InMemoryDataContextRegistry implements DataContextRegistry { - - private final Map<String, Supplier<DataContext>> dataContextIdentifiers; - - public InMemoryDataContextRegistry() { - dataContextIdentifiers = new LinkedHashMap<>(); - } - - @Override - public String registerDataContext(final String dataContextIdentifier, final DataContextDefinition dataContextDef) - throws IllegalArgumentException { - if (dataContextIdentifiers.containsKey(dataContextIdentifier)) { - throw new IllegalArgumentException("DataContext already exist: " + dataContextIdentifier); - } - dataContextIdentifiers.put(dataContextIdentifier, new Supplier<DataContext>() { - @Override - public DataContext get() { - // TODO: Do a proper transformation from definition to instance - return new PojoDataContext(); - } - }); - return dataContextIdentifier; - } - - @Override - public List<String> getDataContextIdentifiers() { - return dataContextIdentifiers.keySet().stream().collect(Collectors.toList()); - } - - @Override - public DataContext openDataContext(String dataContextIdentifier) { - final Supplier<DataContext> supplier = dataContextIdentifiers.get(dataContextIdentifier); - if (supplier == null) { - throw new IllegalArgumentException("No such DataContext: " + dataContextIdentifier); - } - return supplier.get(); - } - -} http://git-wip-us.apache.org/repos/asf/metamodel/blob/8722c196/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 new file mode 100644 index 0000000..f3c45e8 --- /dev/null +++ b/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryDataSourceRegistry.java @@ -0,0 +1,68 @@ +/** + * 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.LinkedHashMap; +import java.util.List; +import java.util.Map; +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 { + + private final Map<String, Supplier<DataContext>> dataSources; + + public InMemoryDataSourceRegistry() { + dataSources = new LinkedHashMap<>(); + } + + @Override + public String registerDataSource(final String dataContextIdentifier, final DataSourceDefinition dataContextDef) + throws IllegalArgumentException { + if (dataSources.containsKey(dataContextIdentifier)) { + throw new IllegalArgumentException("DataContext already exist: " + dataContextIdentifier); + } + dataSources.put(dataContextIdentifier, new Supplier<DataContext>() { + @Override + public DataContext get() { + // TODO: Do a proper transformation from definition to instance + return new PojoDataContext(); + } + }); + return dataContextIdentifier; + } + + @Override + public List<String> getDataSourceNames() { + return dataSources.keySet().stream().collect(Collectors.toList()); + } + + @Override + public DataContext openDataContext(String dataContextIdentifier) { + final Supplier<DataContext> supplier = dataSources.get(dataContextIdentifier); + if (supplier == null) { + throw new IllegalArgumentException("No such DataContext: " + dataContextIdentifier); + } + return supplier.get(); + } + +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/8722c196/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryTenantContext.java ---------------------------------------------------------------------- diff --git a/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryTenantContext.java b/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryTenantContext.java index 99a3e4e..04fb708 100644 --- a/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryTenantContext.java +++ b/service-webapp/src/main/java/org/apache/metamodel/service/app/InMemoryTenantContext.java @@ -21,20 +21,20 @@ package org.apache.metamodel.service.app; public class InMemoryTenantContext implements TenantContext { private final String tenantIdentifier; - private final DataContextRegistry dataContextRegistry; + private final DataSourceRegistry dataContextRegistry; public InMemoryTenantContext(String tenantIdentifier) { this.tenantIdentifier = tenantIdentifier; - this.dataContextRegistry = new InMemoryDataContextRegistry(); + this.dataContextRegistry = new InMemoryDataSourceRegistry(); } @Override - public String getTenantIdentifier() { + public String getTenantName() { return tenantIdentifier; } @Override - public DataContextRegistry getDataContextRegistry() { + public DataSourceRegistry getDataSourceRegistry() { return dataContextRegistry; } http://git-wip-us.apache.org/repos/asf/metamodel/blob/8722c196/service-webapp/src/main/java/org/apache/metamodel/service/app/TenantContext.java ---------------------------------------------------------------------- diff --git a/service-webapp/src/main/java/org/apache/metamodel/service/app/TenantContext.java b/service-webapp/src/main/java/org/apache/metamodel/service/app/TenantContext.java index 4e27922..c550a60 100644 --- a/service-webapp/src/main/java/org/apache/metamodel/service/app/TenantContext.java +++ b/service-webapp/src/main/java/org/apache/metamodel/service/app/TenantContext.java @@ -24,8 +24,8 @@ package org.apache.metamodel.service.app; */ public interface TenantContext { - public String getTenantIdentifier(); + public String getTenantName(); - public DataContextRegistry getDataContextRegistry(); + public DataSourceRegistry getDataSourceRegistry(); } http://git-wip-us.apache.org/repos/asf/metamodel/blob/8722c196/service-webapp/src/main/java/org/apache/metamodel/service/controllers/ColumnController.java ---------------------------------------------------------------------- diff --git a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/ColumnController.java b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/ColumnController.java new file mode 100644 index 0000000..1eb1a13 --- /dev/null +++ b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/ColumnController.java @@ -0,0 +1,87 @@ +/** + * 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 org.apache.metamodel.DataContext; +import org.apache.metamodel.schema.Column; +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.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping(value = { "/{tenant}/{dataContext}/schemas/{schema}/tables/{table}/columns/{column}", + "/{tenant}/{dataContext}/s/{schema}/t/{table}/c/{column}" }, produces = MediaType.APPLICATION_JSON_VALUE) +public class ColumnController { + + private final TenantRegistry tenantRegistry; + + @Autowired + public ColumnController(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, @PathVariable("column") String columnId) { + 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 Column column = table.getColumnByName(columnId); + + final String tenantName = tenantContext.getTenantName(); + final String tableName = table.getName(); + final String schemaName = schema.getName(); + + final Map<String, Object> metadata = new LinkedHashMap<>(); + metadata.put("number", column.getColumnNumber()); + metadata.put("size", column.getColumnSize()); + metadata.put("nullable", column.isNullable()); + metadata.put("primary-key", column.isPrimaryKey()); + metadata.put("indexed", column.isIndexed()); + metadata.put("column-type", column.getType() == null ? null : column.getType().getName()); + metadata.put("native-type", column.getNativeType()); + metadata.put("remarks", column.getRemarks()); + + final Map<String, Object> map = new LinkedHashMap<>(); + map.put("type", "column"); + map.put("name", column.getName()); + map.put("table", tableName); + map.put("schema", schemaName); + map.put("data-context", dataSourceName); + map.put("tenant", tenantName); + map.put("metadata", metadata); + + return map; + } +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/8722c196/service-webapp/src/main/java/org/apache/metamodel/service/controllers/DataContextController.java ---------------------------------------------------------------------- diff --git a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/DataContextController.java b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/DataContextController.java deleted file mode 100644 index 441c74b..0000000 --- a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/DataContextController.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * 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.List; -import java.util.Map; -import java.util.stream.Collectors; - -import javax.validation.Valid; -import javax.ws.rs.core.UriBuilder; - -import org.apache.metamodel.DataContext; -import org.apache.metamodel.service.app.TenantContext; -import org.apache.metamodel.service.app.TenantRegistry; -import org.apache.metamodel.service.controllers.model.RestLink; -import org.apache.metamodel.service.controllers.model.RestDataContextDefinition; -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.ResponseBody; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping(value = "/{tenant}/{dataContext}", produces = MediaType.APPLICATION_JSON_VALUE) -public class DataContextController { - - private final TenantRegistry tenantRegistry; - - @Autowired - public DataContextController(TenantRegistry tenantRegistry) { - this.tenantRegistry = tenantRegistry; - } - - @RequestMapping(method = RequestMethod.PUT) - @ResponseBody - public Map<String, Object> put(@PathVariable("tenant") String tenantId, - @PathVariable("dataContext") String dataContextId, - @Valid @RequestBody RestDataContextDefinition dataContextDefinition) { - final String dataContextIdentifier = tenantRegistry.getTenantContext(tenantId).getDataContextRegistry() - .registerDataContext(dataContextId, dataContextDefinition); - - return get(tenantId, dataContextIdentifier); - } - - @RequestMapping(method = RequestMethod.GET) - @ResponseBody - public Map<String, Object> get(@PathVariable("tenant") String tenantId, - @PathVariable("dataContext") String dataContextId) { - final TenantContext tenantContext = tenantRegistry.getTenantContext(tenantId); - final DataContext dataContext = tenantContext.getDataContextRegistry().openDataContext(dataContextId); - - final String tenantIdentifier = tenantContext.getTenantIdentifier(); - final UriBuilder uriBuilder = UriBuilder.fromPath("/{tenant}/{dataContext}/schemas/{schema}"); - - final List<RestLink> schemaLinks = Arrays.stream(dataContext.getSchemaNames()).map(s -> new RestLink(s, uriBuilder - .build(tenantIdentifier, dataContextId, s))).collect(Collectors.toList()); - - final Map<String, Object> map = new LinkedHashMap<>(); - map.put("type", "data-context"); - map.put("name", dataContextId); - map.put("tenant", tenantIdentifier); - map.put("query", UriBuilder.fromPath("/{tenant}/{dataContext}/query").build(tenantIdentifier, dataContextId)); - map.put("schemas", schemaLinks); - return map; - } -} http://git-wip-us.apache.org/repos/asf/metamodel/blob/8722c196/service-webapp/src/main/java/org/apache/metamodel/service/controllers/DataSourceController.java ---------------------------------------------------------------------- diff --git a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/DataSourceController.java b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/DataSourceController.java new file mode 100644 index 0000000..8be66b1 --- /dev/null +++ b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/DataSourceController.java @@ -0,0 +1,87 @@ +/** + * 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.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.validation.Valid; +import javax.ws.rs.core.UriBuilder; + +import org.apache.metamodel.DataContext; +import org.apache.metamodel.service.app.TenantContext; +import org.apache.metamodel.service.app.TenantRegistry; +import org.apache.metamodel.service.controllers.model.RestLink; +import org.apache.metamodel.service.controllers.model.RestDataSourceDefinition; +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.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping(value = "/{tenant}/{datasource}", produces = MediaType.APPLICATION_JSON_VALUE) +public class DataSourceController { + + private final TenantRegistry tenantRegistry; + + @Autowired + public DataSourceController(TenantRegistry tenantRegistry) { + this.tenantRegistry = tenantRegistry; + } + + @RequestMapping(method = RequestMethod.PUT) + @ResponseBody + public Map<String, Object> put(@PathVariable("tenant") String tenantId, + @PathVariable("datasource") String dataSourceId, + @Valid @RequestBody RestDataSourceDefinition dataContextDefinition) { + final String dataContextIdentifier = tenantRegistry.getTenantContext(tenantId).getDataSourceRegistry() + .registerDataSource(dataSourceId, dataContextDefinition); + + return get(tenantId, dataContextIdentifier); + } + + @RequestMapping(method = RequestMethod.GET) + @ResponseBody + public Map<String, Object> 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<RestLink> schemaLinks = Arrays.stream(dataContext.getSchemaNames()).map(s -> new RestLink(s, uriBuilder + .build(tenantName, dataSourceName, s))).collect(Collectors.toList()); + + final Map<String, Object> map = new LinkedHashMap<>(); + map.put("type", "datasource"); + map.put("name", dataSourceName); + map.put("tenant", tenantName); + map.put("query", UriBuilder.fromPath("/{tenant}/{dataContext}/query").build(tenantName, dataSourceName)); + map.put("schemas", schemaLinks); + return map; + } +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/8722c196/service-webapp/src/main/java/org/apache/metamodel/service/controllers/SchemaController.java ---------------------------------------------------------------------- diff --git a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/SchemaController.java b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/SchemaController.java index 9c18c3b..bfdbe5f 100644 --- a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/SchemaController.java +++ b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/SchemaController.java @@ -40,7 +40,7 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping(value = "/{tenant}/{dataContext}/schemas/{schema}", produces = MediaType.APPLICATION_JSON_VALUE) +@RequestMapping(value = {"/{tenant}/{dataContext}/schemas/{schema}", "/{tenant}/{dataContext}/s/{schema}"}, produces = MediaType.APPLICATION_JSON_VALUE) public class SchemaController { private final TenantRegistry tenantRegistry; @@ -53,23 +53,23 @@ public class SchemaController { @RequestMapping(method = RequestMethod.GET) @ResponseBody public Map<String, Object> get(@PathVariable("tenant") String tenantId, - @PathVariable("dataContext") String dataContextId, @PathVariable("schema") String schemaId) { + @PathVariable("dataContext") String dataSourceName, @PathVariable("schema") String schemaId) { final TenantContext tenantContext = tenantRegistry.getTenantContext(tenantId); - final DataContext dataContext = tenantContext.getDataContextRegistry().openDataContext(dataContextId); + final DataContext dataContext = tenantContext.getDataSourceRegistry().openDataContext(dataSourceName); final Schema schema = dataContext.getSchemaByName(schemaId); - final String tenantIdentifier = tenantContext.getTenantIdentifier(); - final UriBuilder uriBuilder = UriBuilder.fromPath("/{tenant}/{dataContext}/schemas/{schema}/tables/{table}"); + final String tenantName = tenantContext.getTenantName(); + final UriBuilder uriBuilder = UriBuilder.fromPath("/{tenant}/{dataContext}/s/{schema}/t/{table}"); final String schemaName = schema.getName(); final List<RestLink> tableLinks = Arrays.stream(schema.getTableNames()).map(t -> new RestLink(String.valueOf(t), - uriBuilder.build(tenantIdentifier, dataContextId, schemaName, t))).collect(Collectors.toList()); + uriBuilder.build(tenantName, dataSourceName, schemaName, t))).collect(Collectors.toList()); final Map<String, Object> map = new LinkedHashMap<>(); map.put("type", "schema"); map.put("name", schemaName); - map.put("data-context", dataContextId); - map.put("tenant", tenantIdentifier); + map.put("data-context", dataSourceName); + map.put("tenant", tenantName); map.put("tables", tableLinks); return map; } http://git-wip-us.apache.org/repos/asf/metamodel/blob/8722c196/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TableController.java ---------------------------------------------------------------------- diff --git a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TableController.java b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TableController.java index 373c1db..cca0edc 100644 --- a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TableController.java +++ b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TableController.java @@ -41,7 +41,8 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping(value = "/{tenant}/{dataContext}/schemas/{schema}/tables/{table}", produces = MediaType.APPLICATION_JSON_VALUE) +@RequestMapping(value = { "/{tenant}/{dataContext}/schemas/{schema}/tables/{table}", + "/{tenant}/{dataContext}/s/{schema}/t/{table}" }, produces = MediaType.APPLICATION_JSON_VALUE) public class TableController { private final TenantRegistry tenantRegistry; @@ -54,30 +55,29 @@ public class TableController { @RequestMapping(method = RequestMethod.GET) @ResponseBody public Map<String, Object> get(@PathVariable("tenant") String tenantId, - @PathVariable("dataContext") String dataContextId, @PathVariable("schema") String schemaId, + @PathVariable("dataContext") String dataSourceName, @PathVariable("schema") String schemaId, @PathVariable("table") String tableId) { final TenantContext tenantContext = tenantRegistry.getTenantContext(tenantId); - final DataContext dataContext = tenantContext.getDataContextRegistry().openDataContext(dataContextId); + final DataContext dataContext = tenantContext.getDataSourceRegistry().openDataContext(dataSourceName); final Schema schema = dataContext.getSchemaByName(schemaId); final Table table = schema.getTableByName(tableId); - final String tenantIdentifier = tenantContext.getTenantIdentifier(); - final UriBuilder uriBuilder = UriBuilder.fromPath( - "/{tenant}/{dataContext}/schemas/{schema}/tables/{table}/columns/{column}"); + final String tenantName = tenantContext.getTenantName(); + final UriBuilder uriBuilder = UriBuilder.fromPath("/{tenant}/{dataContext}/s/{schema}/t/{table}/c/{column}"); final String tableName = table.getName(); final String schemaName = schema.getName(); - final List<RestLink> columnsLinks = Arrays.stream(table.getColumnNames()).map(c -> new RestLink(String.valueOf(c), - uriBuilder.build(tenantIdentifier, dataContextId, schemaName, tableName, c))).collect(Collectors + final List<RestLink> columnsLinks = Arrays.stream(table.getColumnNames()).map(c -> new RestLink(String.valueOf( + c), uriBuilder.build(tenantName, dataSourceName, schemaName, tableName, c))).collect(Collectors .toList()); final Map<String, Object> map = new LinkedHashMap<>(); map.put("type", "table"); map.put("name", tableName); map.put("schema", schemaName); - map.put("data-context", dataContextId); - map.put("tenant", tenantIdentifier); + map.put("data-context", dataSourceName); + map.put("tenant", tenantName); map.put("columns", columnsLinks); return map; } http://git-wip-us.apache.org/repos/asf/metamodel/blob/8722c196/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TenantController.java ---------------------------------------------------------------------- diff --git a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TenantController.java b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TenantController.java index 48a0ed1..ef3011f 100644 --- a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TenantController.java +++ b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/TenantController.java @@ -55,18 +55,18 @@ public class TenantController { throw new IllegalArgumentException("No such tenant: " + tenantName); } - final String tenantIdentifier = tenantContext.getTenantIdentifier(); + final String tenantNameNormalized = tenantContext.getTenantName(); - final UriBuilder uriBuilder = UriBuilder.fromPath("/{tenant}/{dataContext}"); + final UriBuilder uriBuilder = UriBuilder.fromPath("/{tenant}/{datasource}"); - final List<String> dataContextIdentifiers = tenantContext.getDataContextRegistry().getDataContextIdentifiers(); - final List<RestLink> dataContextLinks = dataContextIdentifiers.stream().map(s -> new RestLink(s, uriBuilder.build( - tenantIdentifier, s))).collect(Collectors.toList()); + final List<String> dataContextIdentifiers = tenantContext.getDataSourceRegistry().getDataSourceNames(); + final List<RestLink> dataSourceLinks = dataContextIdentifiers.stream().map(s -> new RestLink(s, uriBuilder + .build(tenantNameNormalized, s))).collect(Collectors.toList()); final Map<String, Object> map = new LinkedHashMap<>(); map.put("type", "tenant"); - map.put("name", tenantIdentifier); - map.put("data-contexts", dataContextLinks); + map.put("name", tenantNameNormalized); + map.put("datasources", dataSourceLinks); return map; } @@ -74,7 +74,7 @@ public class TenantController { @ResponseBody public Map<String, Object> putTenant(@PathVariable("tenant") String tenantName) { final TenantContext tenantContext = tenantRegistry.createTenantContext(tenantName); - final String tenantIdentifier = tenantContext.getTenantIdentifier(); + final String tenantIdentifier = tenantContext.getTenantName(); final Map<String, Object> map = new LinkedHashMap<>(); map.put("type", "tenant"); http://git-wip-us.apache.org/repos/asf/metamodel/blob/8722c196/service-webapp/src/main/java/org/apache/metamodel/service/controllers/model/RestDataContextDefinition.java ---------------------------------------------------------------------- diff --git a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/model/RestDataContextDefinition.java b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/model/RestDataContextDefinition.java deleted file mode 100644 index ba2002e..0000000 --- a/service-webapp/src/main/java/org/apache/metamodel/service/controllers/model/RestDataContextDefinition.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * 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.model; - -import javax.validation.constraints.NotNull; - -import org.apache.metamodel.service.app.DataContextDefinition; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class RestDataContextDefinition implements DataContextDefinition { - - @JsonProperty(value = "type", required = true) - @NotNull - public String type; - -} http://git-wip-us.apache.org/repos/asf/metamodel/blob/8722c196/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 new file mode 100644 index 0000000..ad5f5ba --- /dev/null +++ b/service-webapp/src/main/java/org/apache/metamodel/service/controllers/model/RestDataSourceDefinition.java @@ -0,0 +1,33 @@ +/** + * 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.model; + +import javax.validation.constraints.NotNull; + +import org.apache.metamodel.service.app.DataSourceDefinition; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class RestDataSourceDefinition implements DataSourceDefinition { + + @JsonProperty(value = "type", required = true) + @NotNull + public String type; + +} http://git-wip-us.apache.org/repos/asf/metamodel/blob/8722c196/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 new file mode 100644 index 0000000..acdbb15 --- /dev/null +++ b/service-webapp/src/test/java/org/apache/metamodel/service/controllers/TenantInteractionScenarioTest.java @@ -0,0 +1,151 @@ +/** + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.util.Map; + +import org.apache.metamodel.service.app.InMemoryTenantRegistry; +import org.apache.metamodel.service.app.TenantRegistry; +import org.junit.Before; +import org.junit.Test; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class TenantInteractionScenarioTest { + + private MockMvc mockMvc; + + @Before + public void init() { + TenantRegistry tenantRegistry = new InMemoryTenantRegistry(); + TenantController tenantController = new TenantController(tenantRegistry); + DataSourceController dataContextController = new DataSourceController(tenantRegistry); + SchemaController schemaController = new SchemaController(tenantRegistry); + TableController tableController = new TableController(tenantRegistry); + ColumnController columnController = new ColumnController(tenantRegistry); + + mockMvc = MockMvcBuilders.standaloneSetup(tenantController, dataContextController, schemaController, + tableController, columnController).build(); + } + + @Test + public void testScenario() throws Exception { + // create tenant + { + final MvcResult result = mockMvc.perform(MockMvcRequestBuilders.put("/tenant1").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("tenant", map.get("type")); + assertEquals("tenant1", map.get("name")); + assertNull(map.get("datasources")); + } + + // create datasource + { + final MvcResult result = mockMvc.perform(MockMvcRequestBuilders.put("/tenant1/mydata").content( + "{'type':'pojo'}".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}]", + map.get("schemas").toString()); + } + + // explore tenant - now with a datasource + { + final MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/tenant1")).andExpect( + MockMvcResultMatchers.status().isOk()).andReturn(); + + final String content = result.getResponse().getContentAsString(); + final Map<?, ?> map = new ObjectMapper().readValue(content, Map.class); + assertEquals("tenant", map.get("type")); + assertEquals("tenant1", map.get("name")); + assertEquals("[{name=mydata, uri=/tenant1/mydata}]", map.get("datasources").toString()); + } + + // explore schema + { + final MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/tenant1/mydata/s/information_schema")) + .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("[{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()); + } + + // explore table + { + final MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get( + "/tenant1/mydata/s/information_schema/t/columns")).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()); + } + + // explore column + { + final MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get( + "/tenant1/mydata/s/information_schema/t/columns/c/nullable")).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( + "{number=4, size=null, nullable=true, primary-key=false, indexed=false, column-type=BOOLEAN, native-type=null, remarks=null}", + map.get("metadata").toString()); + } + } +}
