Repository: incubator-kylin Updated Branches: refs/heads/KYLIN-1011 6d862747d -> 22ef792cf (forced update)
KYLIN-1042, [Data Source]support create Table Schema from Streaming Source Record Project: http://git-wip-us.apache.org/repos/asf/incubator-kylin/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-kylin/commit/1ea58708 Tree: http://git-wip-us.apache.org/repos/asf/incubator-kylin/tree/1ea58708 Diff: http://git-wip-us.apache.org/repos/asf/incubator-kylin/diff/1ea58708 Branch: refs/heads/KYLIN-1011 Commit: 1ea587081743fb5eec00760a494edce0a606b39b Parents: cc15d63 Author: jiazhong <jiazh...@ebay.com> Authored: Fri Sep 18 18:35:30 2015 +0800 Committer: jiazhong <jiazh...@ebay.com> Committed: Wed Sep 23 16:37:11 2015 +0800 ---------------------------------------------------------------------- .../kylin/rest/controller/TableController.java | 27 +- .../kylin/rest/request/StreamingRequest.java | 43 ++ webapp/app/js/controllers/sourceMeta.js | 209 ++++++++- webapp/app/js/model/tableConfig.js | 5 +- webapp/app/js/services/tables.js | 1 + webapp/app/less/app.less | 5 + webapp/app/less/component.less | 4 + webapp/app/partials/cubeDesigner/info.html | 4 +- .../app/partials/modelDesigner/data_model.html | 4 +- .../app/partials/modelDesigner/model_info.html | 4 +- webapp/app/partials/models/models_tree.html | 5 + .../app/partials/tables/source_table_tree.html | 2 +- webapp/app/partials/tables/table_detail.html | 439 ++++++++++++------- 13 files changed, 570 insertions(+), 182 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/1ea58708/server/src/main/java/org/apache/kylin/rest/controller/TableController.java ---------------------------------------------------------------------- diff --git a/server/src/main/java/org/apache/kylin/rest/controller/TableController.java b/server/src/main/java/org/apache/kylin/rest/controller/TableController.java index 133b5ad..39af7db 100644 --- a/server/src/main/java/org/apache/kylin/rest/controller/TableController.java +++ b/server/src/main/java/org/apache/kylin/rest/controller/TableController.java @@ -19,19 +19,18 @@ package org.apache.kylin.rest.controller; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; import org.apache.commons.lang.StringUtils; +import org.apache.kylin.common.KylinConfig; +import org.apache.kylin.common.util.JsonUtil; import org.apache.kylin.metadata.MetadataConstants; +import org.apache.kylin.metadata.MetadataManager; import org.apache.kylin.metadata.model.ColumnDesc; import org.apache.kylin.metadata.model.TableDesc; import org.apache.kylin.rest.exception.InternalErrorException; import org.apache.kylin.rest.request.CardinalityRequest; +import org.apache.kylin.rest.request.StreamingRequest; import org.apache.kylin.rest.response.TableDescResponse; import org.apache.kylin.rest.service.CubeService; import org.slf4j.Logger; @@ -129,6 +128,22 @@ public class TableController extends BasicController { return result; } + + @RequestMapping(value = "/addStreamingSrc", method = { RequestMethod.POST }) + @ResponseBody + public Map<String, String> addStreamingTable(@RequestBody StreamingRequest request) throws IOException { + Map<String, String> result = new HashMap<String, String>(); + String project = request.getProject(); + TableDesc desc = JsonUtil.readValue(request.getTableData(),TableDesc.class); + desc.setUuid(UUID.randomUUID().toString()); + MetadataManager metaMgr = MetadataManager.getInstance(KylinConfig.getInstanceFromEnv()); + metaMgr.saveSourceTable(desc); + cubeMgmtService.syncTableToProject(new String[]{desc.getName()},project); + result.put("success","true"); + return result; + } + + /** * Regenerate table cardinality * http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/1ea58708/server/src/main/java/org/apache/kylin/rest/request/StreamingRequest.java ---------------------------------------------------------------------- diff --git a/server/src/main/java/org/apache/kylin/rest/request/StreamingRequest.java b/server/src/main/java/org/apache/kylin/rest/request/StreamingRequest.java new file mode 100644 index 0000000..3bcf9d7 --- /dev/null +++ b/server/src/main/java/org/apache/kylin/rest/request/StreamingRequest.java @@ -0,0 +1,43 @@ +/* + * 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.kylin.rest.request; + +import java.lang.String;public class StreamingRequest { + + private String project; + + private String tableData; + + public String getProject() { + return project; + } + + public void setProject(String project) { + this.project = project; + } + + public String getTableData() { + return tableData; + } + + public void setTableData(String tableData) { + this.tableData = tableData; + } +} http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/1ea58708/webapp/app/js/controllers/sourceMeta.js ---------------------------------------------------------------------- diff --git a/webapp/app/js/controllers/sourceMeta.js b/webapp/app/js/controllers/sourceMeta.js index 942c079..9d405dc 100755 --- a/webapp/app/js/controllers/sourceMeta.js +++ b/webapp/app/js/controllers/sourceMeta.js @@ -152,12 +152,211 @@ KylinApp }) } }; - $scope.trimType = function (typeName) { - if (typeName.match(/VARCHAR/i)) { - typeName = "VARCHAR"; + + + //streaming model + $scope.openStreamingSourceModal = function () { + $modal.open({ + templateUrl: 'addStreamingSource.html', + controller: StreamingSourceCtrl, + resolve: { + tableNames: function () { + return $scope.tableNames; + }, + projectName: function () { + return $scope.projectModel.selectedProject; + }, + scope: function () { + return $scope; + } + } + }); + }; + + var StreamingSourceCtrl = function ($scope, $location, $modalInstance, tableNames, MessageService, projectName, scope, tableConfig) { + $scope.streamingPrefix = "STREAMING_"; + $scope.projectName = projectName; + $scope.tableConfig = tableConfig; + $scope.streaming = { + sourceSchema: '', + 'parseResult': {} } - return typeName.trim().toLowerCase(); - } + $scope.table = { + name: '', + sourceValid:false + } + + $scope.cancel = function () { + $modalInstance.dismiss('cancel'); + }; + + $scope.streamingOnLoad = function () { + console.log($scope.streaming.sourceSchema); + } + + $scope.columnList = []; + + $scope.streamingOnChange = function () { + console.log($scope.streaming.sourceSchema); + try { + $scope.streaming.parseResult = JSON.parse($scope.streaming.sourceSchema); + } catch (error) { + $scope.table.sourceValid = false; + console.log(error); + return; + } + $scope.table.sourceValid = true; + var columnList = []; + for (var key in $scope.streaming.parseResult) { + var defaultType="varchar(256)"; + var _value = $scope.streaming.parseResult[key]; + var defaultChecked = "Y"; + if(typeof _value ==="string"){ + defaultType="varchar(256)"; + }else if(typeof _value ==="number"){ + if(_value <= 2147483647){ + if(_value.toString().indexOf(".")!=-1){ + defaultType="decimal"; + }else{ + defaultType="int"; + } + }else{ + defaultType="timestamp"; + } + } + if(defaultType=="timestamp"){ + defaultChecked = "N"; + } + columnList.push({ + 'name': key, + 'checked': defaultChecked, + 'type': defaultType, + 'fromSource':'Y' + }); + + + + //var formatList = []; + //var + columnList = _.sortBy(columnList, function (i) { return i.type; }); + } + + var timeMeasure = ['year_start','month_start','day_start','hour_start','min_start']; + for(var i = 0;i<timeMeasure.length;i++){ + var defaultCheck = 'Y'; + if(timeMeasure[i]=='min_start'){ + defaultCheck = 'N'; + } + columnList.push({ + 'name': timeMeasure[i], + 'checked': defaultCheck, + 'type': 'timestamp', + 'fromSource':'N' + }); + } + + if($scope.columnList.length==0){ + $scope.columnList = columnList; + } + + angular.forEach(columnList,function(item){ + var included = false; + for(var i=0;i<$scope.columnList.length;i++){ + if($scope.columnList[i].name==item.name){ + included = true; + break; + } + } + if(!included){ + $scope.columnList.push(item); + } + }) + + } + + $scope.form={}; + $scope.rule={ + 'timestampColumnConflict':false + } + $scope.syncStreamingSchema = function () { + $scope.form['setStreamingSchema'].$sbumitted = true; + if(!$scope.streaming.sourceSchema||$scope.streaming.sourceSchema===""){ + return; + } + + if(!$scope.table.name||$scope.table.name===""){ + return; + } + + var timestampCount = 0; + angular.forEach($scope.columnList,function(item){ + if(item.checked == "Y"&&item.type=="timestamp"&&item.fromSource=='Y'){ + timestampCount++; + } + }) + + if(timestampCount!=1){ + $scope.rule.timestampColumnConflict = true; + return; + } + + var columns = []; + angular.forEach($scope.columnList,function(column,$index){ + if (column.checked == "Y") { + var columnInstance = { + "id": ++$index, + "name": column.name, + "datatype": column.type + } + columns.push(columnInstance); + } + }) + + + $scope.tableData = { + "name": $scope.streamingPrefix+$scope.table.name, + "columns": columns, + 'database':'Default' + } + + SweetAlert.swal({ + title: '', + text: 'Are you sure to create the streaming table info?', + type: '', + showCancelButton: true, + confirmButtonColor: '#DD6B55', + confirmButtonText: "Yes", + closeOnConfirm: true + }, function (isConfirm) { + if (isConfirm) { + loadingRequest.show(); + TableService.addStreamingSrc({}, {project: $scope.projectName,tableData:angular.toJson($scope.tableData)}, function (request) { + if(request.success){ + loadingRequest.hide(); + SweetAlert.swal('', 'Create Streaming Table Schema Successfully.', 'success'); + $scope.cancel(); + scope.aceSrcTbLoaded(true); + return; + }else{ + SweetAlert.swal('Oops...', "Failed to take action.", 'error'); + } + //end loading + loadingRequest.hide(); + }, function (e) { + if (e.data && e.data.exception) { + var message = e.data.exception; + var msg = !!(message) ? message : 'Failed to take action.'; + SweetAlert.swal('Oops...', msg, 'error'); + } else { + SweetAlert.swal('Oops...', "Failed to take action.", 'error'); + } + loadingRequest.hide(); + }); + } + }); + } + }; + }); http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/1ea58708/webapp/app/js/model/tableConfig.js ---------------------------------------------------------------------- diff --git a/webapp/app/js/model/tableConfig.js b/webapp/app/js/model/tableConfig.js index f994b09..3dd7b25 100644 --- a/webapp/app/js/model/tableConfig.js +++ b/webapp/app/js/model/tableConfig.js @@ -22,6 +22,9 @@ KylinApp.constant('tableConfig', { {attr: 'name', name: 'Name'}, {attr: 'datatype', name: 'Data Type'}, {attr: 'cardinality', name: 'Cardinality'} - ] + ], + dataTypes:["tinyint","smallint","int","bigint","float","double","decimal","timestamp","date","string","varchar(256)","char","boolean","binary"] + + }); http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/1ea58708/webapp/app/js/services/tables.js ---------------------------------------------------------------------- diff --git a/webapp/app/js/services/tables.js b/webapp/app/js/services/tables.js index 1f1f15a..3b5e9f4 100755 --- a/webapp/app/js/services/tables.js +++ b/webapp/app/js/services/tables.js @@ -23,6 +23,7 @@ KylinApp.factory('TableService', ['$resource', function ($resource, config) { getExd: {method: 'GET', params: {action: 'exd-map'}, isArray: false}, reload: {method: 'PUT', params: {action: 'reload'}, isArray: false}, loadHiveTable: {method: 'POST', params: {}, isArray: false}, + addStreamingSrc: {method: 'POST', params: {action:'addStreamingSrc'}, isArray: false}, genCardinality: {method: 'PUT', params: {action: 'cardinality'}, isArray: false} }); }]); http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/1ea58708/webapp/app/less/app.less ---------------------------------------------------------------------- diff --git a/webapp/app/less/app.less b/webapp/app/less/app.less index a7651fe..fc80698 100644 --- a/webapp/app/less/app.less +++ b/webapp/app/less/app.less @@ -661,3 +661,8 @@ ul.messenger .messenger-message-inner,.ngCellText { -o-transition: width 0.5s;; transition: width 0.5s;; } + +.form-group.required .control-label:after { + content:"*"; + color:red; +} http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/1ea58708/webapp/app/less/component.less ---------------------------------------------------------------------- diff --git a/webapp/app/less/component.less b/webapp/app/less/component.less index a160752..929006f 100644 --- a/webapp/app/less/component.less +++ b/webapp/app/less/component.less @@ -996,3 +996,7 @@ ul.abn-tree li.abn-tree-row a{ .sweet-alert .lead.text-muted{ word-break:break-all; } + +.modal-body.streaming-source .ace_editor{ + height: 600px !important; +} http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/1ea58708/webapp/app/partials/cubeDesigner/info.html ---------------------------------------------------------------------- diff --git a/webapp/app/partials/cubeDesigner/info.html b/webapp/app/partials/cubeDesigner/info.html index 1684516..f680487 100644 --- a/webapp/app/partials/cubeDesigner/info.html +++ b/webapp/app/partials/cubeDesigner/info.html @@ -20,7 +20,7 @@ <div class="col-xs-8"> <ng-form name="forms.cube_info_form" novalidate> <!--Project--> - <div class="form-group"> + <div class="form-group required"> <div class="row"> <label class="col-xs-12 col-sm-3 control-label no-padding-right"> <b>Model Name</b> @@ -40,7 +40,7 @@ </div> <!--Cube Name--> - <div class="form-group"> + <div class="form-group required"> <div class="row"> <label class="col-xs-12 col-sm-3 control-label no-padding-right font-color-default"> <b>Cube Name</b> http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/1ea58708/webapp/app/partials/modelDesigner/data_model.html ---------------------------------------------------------------------- diff --git a/webapp/app/partials/modelDesigner/data_model.html b/webapp/app/partials/modelDesigner/data_model.html index 60cbacb..e100287 100644 --- a/webapp/app/partials/modelDesigner/data_model.html +++ b/webapp/app/partials/modelDesigner/data_model.html @@ -20,9 +20,9 @@ <ng-form name="forms.data_model_form"> <!-- Fact Table Name --> - <div class="form-group"> + <div class="form-group required"> <div class="row"> - <label class="col-xs-12 col-sm-2 concube.detailtrol-label no-padding-right font-color-default"> + <label class="col-xs-12 col-sm-2 control-label concube.detailtrol-label no-padding-right font-color-default"> <b>Fact Table</b> </label> <div class="col-xs-12 col-sm-6" ng-class="{'has-error':forms.data_model_form.innerform.typeahead.$invalid && (forms.data_model_form.innerform.typeahead.$dirty||forms.data_model_form.$sbumitted)}"> http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/1ea58708/webapp/app/partials/modelDesigner/model_info.html ---------------------------------------------------------------------- diff --git a/webapp/app/partials/modelDesigner/model_info.html b/webapp/app/partials/modelDesigner/model_info.html index 18fac09..aed4325 100644 --- a/webapp/app/partials/modelDesigner/model_info.html +++ b/webapp/app/partials/modelDesigner/model_info.html @@ -22,7 +22,7 @@ <ng-form name="forms.model_info_form" novalidate> <!--Model Name--> - <div class="form-group"> + <div class="form-group required"> <div class="row"> <label class="col-xs-12 col-sm-3 control-label no-padding-right font-color-default"> <b>Model Name</b> @@ -96,4 +96,4 @@ </div><!-- /.box-body --> </div> </div> -</div> \ No newline at end of file +</div> http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/1ea58708/webapp/app/partials/models/models_tree.html ---------------------------------------------------------------------- diff --git a/webapp/app/partials/models/models_tree.html b/webapp/app/partials/models/models_tree.html index b04d481..722d65f 100644 --- a/webapp/app/partials/models/models_tree.html +++ b/webapp/app/partials/models/models_tree.html @@ -43,6 +43,11 @@ <li ng-if="userService.hasRole('ROLE_ADMIN')"> <a href="models/add" ng-if="userService.hasRole('ROLE_MODELER')"><i class="fa fa-star"></i> New Model</a> </li> + + <li ng-if="userService.hasRole('ROLE_ADMIN')"> + <a href="streaming/add" ng-if="userService.hasRole('ROLE_MODELER')"><i class="fa fa-area-chart"></i>New Streaming</a> + </li> + </ul> </div> http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/1ea58708/webapp/app/partials/tables/source_table_tree.html ---------------------------------------------------------------------- diff --git a/webapp/app/partials/tables/source_table_tree.html b/webapp/app/partials/tables/source_table_tree.html index 2e5de36..ff95d10 100755 --- a/webapp/app/partials/tables/source_table_tree.html +++ b/webapp/app/partials/tables/source_table_tree.html @@ -26,7 +26,7 @@ <div class="col-xs-5" style="padding-left: 0px;margin-top: 20px;"> <div class="pull-right"> <a class="btn btn-xs btn-primary" tooltip="Load Hive Table" ng-if="userService.hasRole('ROLE_ADMIN')" ng-click="openModal()"><i class="fa fa-download"></i></a> - <a class="btn btn-xs btn-success" tooltip="Refresh Tables" ng-click="aceSrcTbChanged()"><i class="fa fa-refresh"></i></a> + <a class="btn btn-xs btn-primary" tooltip="Add Streaming Table" ng-if="userService.hasRole('ROLE_ADMIN')" ng-click="openStreamingSourceModal()"><i class="fa fa-area-chart"></i></a> </div> </div> http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/1ea58708/webapp/app/partials/tables/table_detail.html ---------------------------------------------------------------------- diff --git a/webapp/app/partials/tables/table_detail.html b/webapp/app/partials/tables/table_detail.html index dc1492d..e692269 100644 --- a/webapp/app/partials/tables/table_detail.html +++ b/webapp/app/partials/tables/table_detail.html @@ -16,187 +16,300 @@ * limitations under the License. --> -<div ng-controller="SourceMetaCtrl" class="nav-tabs-custom"> - <div class="col-xs-12" ng-show="tableModel.selectedSrcDb&&tableModel.selectedSrcTable.name"> - <h3 class="text-info">Table Schema:{{ tableModel.selectedSrcTable.name}}</h3> - <div class="tabbable nav-tabs-custom"> - <ul class="nav nav-tabs"> - <li class="active"> - <a data-toggle="tab" href="#column">Columns</a> - </li> - <li> - <a data-toggle="tab" href="#schema">Extend Information</a> - </li> - </ul> - <div class="tab-content"> - <!--Schema--> - <div id="schema" class="tab-pane"> - <div ng-if="tableModel.selectedSrcTable.uuid" class="table-responsive"> - <table class="table"> - <tbody> - <tr> - <th style="width:20%">NAME</th> - <td>{{ tableModel.selectedSrcTable.name}}</td> - </tr> - <tr> - <th>Hive DATABASE</th> - <td>{{tableModel.selectedSrcTable.database}}</td> - </tr> - <tr> - <th>SNAPSHOT TIME</th> - <td>{{tableModel.selectedSrcTable.exd.lastUpdateTime | utcToConfigTimeZone}}</td> - </tr> - <tr> - <th>LOCATION</th> - <td>{{tableModel.selectedSrcTable.exd.location}}</td> - </tr> - <tr> - <th>INPUT FORMAT</th> - <td>{{tableModel.selectedSrcTable.exd.inputformat}}</td> - </tr> - <tr> - <th>OUTPUT FORMAT</th> - <td>{{tableModel.selectedSrcTable.exd.outputformat}}</td> - </tr> - <tr> - <th>OWNER</th> - <td><a href="mailto:{{tableModel.selectedSrcTable.exd.owner}}">{{tableModel.selectedSrcTable.exd.owner}}</a></td> - </tr> - <tr> - <th>TOTAL FILE NUMBER</th> - <td>{{tableModel.selectedSrcTable.exd.totalNumberFiles}}</td> - </tr> - <tr> - <th>TOTAL FILE SIZE</th> - <td>{{tableModel.selectedSrcTable.exd.totalFileSize}}</td> - </tr> - <tr> - <th>PARTITIONED</th> - <td>{{tableModel.selectedSrcTable.exd.partitioned}}</td> - </tr> - <tr> - <th>PARTITION COLUMNS</th> - <td>{{tableModel.selectedSrcTable.exd.partitionColumns}}</td> - </tr> - </tbody> - </table> - </div> - </div> - <!--Columns--> - <div id="column" class="tab-pane active"> - <div class="profile-user-info"> - <div> - <label class="table-header-text">Columns</label> +<div ng-controller="SourceMetaCtrl" class="nav-tabs-custom"> + <div class="col-xs-12" ng-show="tableModel.selectedSrcDb&&tableModel.selectedSrcTable.name"> + <h3 class="text-info">Table Schema:{{ tableModel.selectedSrcTable.name}}</h3> + + <div class="tabbable nav-tabs-custom"> + <ul class="nav nav-tabs"> + <li class="active"> + <a data-toggle="tab" href="#column">Columns</a> + </li> + <li> + <a data-toggle="tab" href="#schema">Extend Information</a> + </li> + </ul> + <div class="tab-content"> + <!--Schema--> + <div id="schema" class="tab-pane"> + <div ng-if="tableModel.selectedSrcTable.uuid" class="table-responsive"> + <table class="table"> + <tbody> + <tr> + <th style="width:20%">NAME</th> + <td>{{ tableModel.selectedSrcTable.name}}</td> + </tr> + <tr> + <th>Hive DATABASE</th> + <td>{{tableModel.selectedSrcTable.database}}</td> + </tr> + <tr> + <th>SNAPSHOT TIME</th> + <td>{{tableModel.selectedSrcTable.exd.lastUpdateTime | utcToConfigTimeZone}}</td> + </tr> + <tr> + <th>LOCATION</th> + <td>{{tableModel.selectedSrcTable.exd.location}}</td> + </tr> + <tr> + <th>INPUT FORMAT</th> + <td>{{tableModel.selectedSrcTable.exd.inputformat}}</td> + </tr> + <tr> + <th>OUTPUT FORMAT</th> + <td>{{tableModel.selectedSrcTable.exd.outputformat}}</td> + </tr> + <tr> + <th>OWNER</th> + <td><a + href="mailto:{{tableModel.selectedSrcTable.exd.owner}}">{{tableModel.selectedSrcTable.exd.owner}}</a> + </td> + </tr> + <tr> + <th>TOTAL FILE NUMBER</th> + <td>{{tableModel.selectedSrcTable.exd.totalNumberFiles}}</td> + </tr> + <tr> + <th>TOTAL FILE SIZE</th> + <td>{{tableModel.selectedSrcTable.exd.totalFileSize}}</td> + </tr> + <tr> + <th>PARTITIONED</th> + <td>{{tableModel.selectedSrcTable.exd.partitioned}}</td> + </tr> + <tr> + <th>PARTITION COLUMNS</th> + <td>{{tableModel.selectedSrcTable.exd.partitionColumns}}</td> + </tr> + </tbody> + </table> + </div> + </div> + <!--Columns--> + <div id="column" class="tab-pane active"> + <div class="profile-user-info"> + <div> + <label class="table-header-text">Columns</label> <span class="input-icon form-search nav-search pull-right"> - <input type="text" placeholder="Filter ..." class="nav-search-input" ng-model="columnName"/> + <input type="text" placeholder="Filter ..." class="nav-search-input" + ng-model="columnName"/> <i class="ace-icon fa fa-search nav-search-icon"></i> </span> - </div> - <div class="space-6"></div> - <div ng-if="(tableModel.selectedSrcTable.columns | filter: columnName).length>0"> - <table class="table table-hover table-striped list"> - <thead> - <tr style="cursor: pointer"> - <th ng-repeat="theaditem in tableConfig.theaditems" - ng-click="state.filterAttr= theaditem.attr;state.reverseColumn=theaditem.attr;state.filterReverse=!state.filterReverse;"> - {{theaditem.name}} - <i ng-if="state.reverseColumn!= theaditem.attr" - class="fa fa-unsorted"></i> - <i ng-if="state.reverseColumn== theaditem.attr && !state.filterReverse" - class="fa fa-sort-asc"></i> - <i ng-if="state.reverseColumn== theaditem.attr && state.filterReverse" - class="fa fa-sort-desc"></i> - </th> - </tr> - </thead> - - <tr ng-repeat="column in tableModel.selectedSrcTable.columns | filter: columnName | orderObjectBy:state.filterAttr:state.filterReverse"> - <td style="{{(tableModel.selectedSrcTable.selectedSrcColumn.id == column.id)? 'background-color:#EBF9FE':''}}"> - {{ column.id}} - </td> - <td style="{{(tableModel.selectedSrcTable.selectedSrcColumn.id == column.id)? 'background-color:#EBF9FE':''}}"> - {{ column.name}} - </td> - <td style="{{(tableModel.selectedSrcTable.selectedSrcColumn.id == column.id)? 'background-color:#EBF9FE':''}}"> - {{ column.datatype}} - </td> - <td style="{{(tableModel.selectedSrcTable.selectedSrcColumn.id == column.id)? 'background-color:#EBF9FE':''}}"> - <!--{{ tableModel.selectedSrcTable.cardinality[column.name]}}--> - {{column.cardinality}} - </td> - </tr> - </table> - </div> - <div ng-if="(tableModel.selectedSrcTable.columns | filter: columnName).length == 0" no-result - text="No Matched Table Column."></div> - <div ng-if="!!!tableModel.selectedSrcTable.uuid"> - <div no-result text="No Table Selected."></div> - </div> - </div> - </div> </div> + <div class="space-6"></div> + <div ng-if="(tableModel.selectedSrcTable.columns | filter: columnName).length>0"> + <table class="table table-hover table-striped list"> + <thead> + <tr style="cursor: pointer"> + <th ng-repeat="theaditem in tableConfig.theaditems" + ng-click="state.filterAttr= theaditem.attr;state.reverseColumn=theaditem.attr;state.filterReverse=!state.filterReverse;"> + {{theaditem.name}} + <i ng-if="state.reverseColumn!= theaditem.attr" + class="fa fa-unsorted"></i> + <i ng-if="state.reverseColumn== theaditem.attr && !state.filterReverse" + class="fa fa-sort-asc"></i> + <i ng-if="state.reverseColumn== theaditem.attr && state.filterReverse" + class="fa fa-sort-desc"></i> + </th> + </tr> + </thead> + + <tr + ng-repeat="column in tableModel.selectedSrcTable.columns | filter: columnName | orderObjectBy:state.filterAttr:state.filterReverse"> + <td + style="{{(tableModel.selectedSrcTable.selectedSrcColumn.id == column.id)? 'background-color:#EBF9FE':''}}"> + {{ column.id}} + </td> + <td + style="{{(tableModel.selectedSrcTable.selectedSrcColumn.id == column.id)? 'background-color:#EBF9FE':''}}"> + {{ column.name}} + </td> + <td + style="{{(tableModel.selectedSrcTable.selectedSrcColumn.id == column.id)? 'background-color:#EBF9FE':''}}"> + {{ column.datatype}} + </td> + <td + style="{{(tableModel.selectedSrcTable.selectedSrcColumn.id == column.id)? 'background-color:#EBF9FE':''}}"> + <!--{{ tableModel.selectedSrcTable.cardinality[column.name]}}--> + {{column.cardinality}} + </td> + </tr> + </table> + </div> + <div ng-if="(tableModel.selectedSrcTable.columns | filter: columnName).length == 0" no-result + text="No Matched Table Column."></div> + <div ng-if="!!!tableModel.selectedSrcTable.uuid"> + <div no-result text="No Table Selected."></div> + </div> + </div> </div> + </div> </div> + </div> - <!--show load hive table tip when no models list--> - <div ng-show="!tableModel.selectedSrcDb.length" style="margin-top:40px;"> + <!--show load hive table tip when no models list--> + <div ng-show="!tableModel.selectedSrcDb.length" style="margin-top:40px;"> - <!--project selected tip--> - <div ng-if="projectModel.getSelectedProject();" class="box box-primary"> - <div class="box-header with-border"> - <i class="fa fa-bullhorn"></i> - <h3 class="box-title">No tables</h3> - </div> - <div class="box-body"> - <div> - <a tooltip="Load Hive Table" href="javascript:void(0);" ng-if="userService.hasRole('ROLE_MODELER')" ng-click="openModal()">Click here to load your hive table</a> - </div> - </div><!-- /.box-body --> - </div> - <!--roject not selected tip--> - <div ng-if="!projectModel.getSelectedProject();" class="box box-primary"> - <div class="box-header with-border"> - <i class="icon fa fa-warning"></i> - <h3 class="box-title">No project selected</h3> - </div> - <div class="box-body"> - <div class="callout callout-danger"> - <p class="text-danger"> - Please select your project first - </p> - </div> - </div><!-- /.box-body --> + <!--project selected tip--> + <div ng-if="projectModel.getSelectedProject();" class="box box-primary"> + <div class="box-header with-border"> + <i class="fa fa-bullhorn"></i> + + <h3 class="box-title">No tables</h3> + </div> + <div class="box-body"> + <div> + <a tooltip="Load Hive Table" href="javascript:void(0);" ng-if="userService.hasRole('ROLE_MODELER')" + ng-click="openModal()">Click here to load your hive table</a> </div> + </div> + <!-- /.box-body --> </div> + <!--roject not selected tip--> + <div ng-if="!projectModel.getSelectedProject();" class="box box-primary"> + <div class="box-header with-border"> + <i class="icon fa fa-warning"></i> - <!--show load hive table tip when no models list--> - <div ng-show="tableModel.selectedSrcDb.length&&!tableModel.selectedSrcTable.name" style="margin-top:40px;"> - <div class="box box-primary"> - <div class="box-header with-border"> - <i class="fa fa-bullhorn"></i> - <h3 class="box-title">No table selected</h3> - </div> - <div class="box-body"> - Select your table - </div><!-- /.box-body --> + <h3 class="box-title">No project selected</h3> + </div> + <div class="box-body"> + <div class="callout callout-danger"> + <p class="text-danger"> + Please select your project first + </p> </div> + </div> + <!-- /.box-body --> </div> + </div> + <!--show load hive table tip when no models list--> + <div ng-show="tableModel.selectedSrcDb.length&&!tableModel.selectedSrcTable.name" style="margin-top:40px;"> + <div class="box box-primary"> + <div class="box-header with-border"> + <i class="fa fa-bullhorn"></i> - <script type="text/ng-template" id="addHiveTable.html"> - <div class="modal-header"> - <h4>Load Hive Table Metadata</h4> - </div> - <div class="modal-body"> - <span><strong>Project: </strong>{{ $parent.projectName!=null?$parent.projectName:'NULL'}}</span> - <label for="tables"> Table Names:(Seperate with comma)</label> + <h3 class="box-title">No table selected</h3> + </div> + <div class="box-body"> + Select your table + </div> + <!-- /.box-body --> + </div> + </div> + + + <script type="text/ng-template" id="addHiveTable.html"> + <div class="modal-header"> + <h4>Load Hive Table Metadata</h4> + </div> + <div class="modal-body"> + <span><strong>Project: </strong>{{ $parent.projectName!=null?$parent.projectName:'NULL'}}</span> + <label for="tables"> Table Names:(Seperate with comma)</label> <textarea ng-model="$parent.tableNames" class="form-control" id="tables" placeholder="table1,table2 By default,system will choose 'Default' as database,you can specify database like this 'database.table'"></textarea> + </div> + <div class="modal-footer"> + <button class="btn btn-primary" ng-click="add()">Sync</button> + <button class="btn btn-primary" ng-click="cancel()">Cancel</button> + </div> + </script> + + <script type="text/ng-template" id="addStreamingSource.html"> + <div class="modal-header"> + <h2>Create Streaming Table Schema</h2> + </div> + + <div class="modal-body streaming-source" style="height: 480px;"> + <div class="col-xs-5"> + <p class="text-info"> + Need to input streaming source record here, will detect the source schema and create a table schema for + streaming. + </p> + + <div style="padding:15px;" class="has-error"> + <small class="help-block" ng-show="streaming.sourceSchema==''&&form.setStreamingSchema.$sbumitted">Please + input Streaming source record to generate schema. + </small> + </div> + <div style="margin-bottom: 20px;"> + <span class="label label-info">JSON</span> </div> - <div class="modal-footer"> - <button class="btn btn-primary" ng-click="add()">Sync</button> - <button class="btn btn-primary" ng-click="cancel()">Cancel</button> + <div ng-model="streaming.sourceSchema" ui-ace="{ + useWrapMode : true, + mode:'json', + onLoad: streamingOnLoad + }"> + </div> - </script> + </div> + <div class="col-xs-1" style="margin-top:300px;text-align:center;"> + <button type="button" class="btn btn-primary" ng-click="streamingOnChange()"><i + class="fa fa-angle-double-right fa-5" style="font-size:2em;"></i></button> + </div> + <div class="col-xs-6" ng-show="table.sourceValid"> + <ol class="text-info" style="margin-bottom: 30px;"> + <li>Choose one 'timestamp' type column for streaming table.</li> + <li>Uncheck the 'timestamp' type column which will not be used.</li> + </ol> + <form class="form-horizontal" name="form.setStreamingSchema" novalidate> + <div class="form-group required"> + <label class="col-xs-4 control-label" style="text-align: left;">Table Name</label> + + <div class="col-xs-8" + ng-class="{'has-error':form.setStreamingSchema.streamingObject.$invalid && (form.setStreamingSchema.streamingObject.$dirty||form.setStreamingSchema.$sbumitted)}"> + <input type="text" name="streamingObject" required="" ng-model="table.name" class="form-control"/> + <small class="help-block" + ng-show="form.setStreamingSchema.streamingObject.$error.required&&(form.setStreamingSchema.streamingObject.$dirty||form.setStreamingSchema.$sbumitted)"> + Table name is required. + </small> + </div> + </div> + </form> + <table class="table table-hover table-bordered"> + <tr> + <th>Check As Column</th> + <th>Column</th> + <th>Column Type</th> + <th>Comment</th> + </tr> + <tr ng-repeat="column in columnList"> + <td><label style="width:100%;cursor: pointer;" for="{{column.name}}"><input style="width:1em;height:1em;" + type="checkbox" + id="{{column.name}}" + ng-model="column.checked" + ng-true-value="Y" + ng-false-value="N"/></label> + </td> + <td>{{column.name}}</td> + <td> + <select chosen ng-model="column.type" + ng-options="type as type for type in tableConfig.dataTypes" + data-placeholder="select a column type" + style="width: 200px !important;" + class="chosen-select"> + </select> + </td> + <td> + <label ng-if="column.type=='timestamp'&&column.fromSource=='Y'" class="badge badge-info">TIMESTAMP</label> + <label ng-if="column.fromSource=='N'" class="badge badge-info">AUTO APPEND</label> + </td> + </tr> + </table> + + <div class="has-error" ng-if="rule.timestampColumnConflict"> + <small class="help-block"> + You should choose one, and only one 'timestamp' type column generated from source schema. + </small> + </div> + </div> + </div> + <div class="modal-footer"> + <button class="btn btn-primary" ng-click="syncStreamingSchema()" ng-disabled="form.setStreamingSchema.$invalid"> + Submit + </button> + <button class="btn btn-primary" ng-click="cancel()">Cancel</button> + </div> + </script> </div>