Repository: kylin Updated Branches: refs/heads/2.x-staging d829340c5 -> 192bbf913
KYLIN-1128 cube,model metadata clone Project: http://git-wip-us.apache.org/repos/asf/kylin/repo Commit: http://git-wip-us.apache.org/repos/asf/kylin/commit/192bbf91 Tree: http://git-wip-us.apache.org/repos/asf/kylin/tree/192bbf91 Diff: http://git-wip-us.apache.org/repos/asf/kylin/diff/192bbf91 Branch: refs/heads/2.x-staging Commit: 192bbf9130d04cd42fcdf1a0235e824da455a799 Parents: d829340 Author: jian <zhongj...@apache.org> Authored: Fri Jan 22 01:20:39 2016 +0800 Committer: jian <zhongj...@apache.org> Committed: Fri Jan 22 01:20:39 2016 +0800 ---------------------------------------------------------------------- .../org/apache/kylin/cube/model/CubeDesc.java | 1 + .../kylin/rest/controller/CubeController.java | 167 ++++++++++++++++--- .../kylin/rest/controller/ModelController.java | 46 ++++- webapp/app/js/controllers/cubes.js | 69 ++++++++ webapp/app/js/controllers/models.js | 67 ++++++++ webapp/app/js/services/cubes.js | 1 + webapp/app/js/services/models.js | 1 + webapp/app/less/app.less | 8 + webapp/app/partials/cubes/cube_clone.html | 63 +++++++ webapp/app/partials/cubes/cubes.html | 2 + webapp/app/partials/models/model_clone.html | 63 +++++++ webapp/app/partials/models/models.html | 1 + webapp/app/partials/models/models_tree.html | 7 +- 13 files changed, 468 insertions(+), 28 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/kylin/blob/192bbf91/core-cube/src/main/java/org/apache/kylin/cube/model/CubeDesc.java ---------------------------------------------------------------------- diff --git a/core-cube/src/main/java/org/apache/kylin/cube/model/CubeDesc.java b/core-cube/src/main/java/org/apache/kylin/cube/model/CubeDesc.java index 2c1e623..fe19ac9 100644 --- a/core-cube/src/main/java/org/apache/kylin/cube/model/CubeDesc.java +++ b/core-cube/src/main/java/org/apache/kylin/cube/model/CubeDesc.java @@ -810,6 +810,7 @@ public class CubeDesc extends RootPersistentEntity { newCubeDesc.setRetentionRange(cubeDesc.getRetentionRange()); newCubeDesc.setEngineType(cubeDesc.getEngineType()); newCubeDesc.setStorageType(cubeDesc.getStorageType()); + newCubeDesc.setAggregationGroups(cubeDesc.getAggregationGroups()); newCubeDesc.setConfig(cubeDesc.getConfig()); newCubeDesc.updateRandomUuid(); return newCubeDesc; http://git-wip-us.apache.org/repos/asf/kylin/blob/192bbf91/server/src/main/java/org/apache/kylin/rest/controller/CubeController.java ---------------------------------------------------------------------- diff --git a/server/src/main/java/org/apache/kylin/rest/controller/CubeController.java b/server/src/main/java/org/apache/kylin/rest/controller/CubeController.java index f9ac14e..9afa750 100644 --- a/server/src/main/java/org/apache/kylin/rest/controller/CubeController.java +++ b/server/src/main/java/org/apache/kylin/rest/controller/CubeController.java @@ -18,7 +18,7 @@ package org.apache.kylin.rest.controller; -import java.io.IOException; +import java.io.*; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Iterator; @@ -258,6 +258,123 @@ public class CubeController extends BasicController { } } + + @RequestMapping(value = "/{cubeName}/clone", method = {RequestMethod.PUT}) + @ResponseBody + public CubeInstance cloneCube(@PathVariable String cubeName, @RequestBody CubeRequest cubeRequest) { + String newCubeName = cubeRequest.getCubeName(); + String project = cubeRequest.getProject(); + + CubeInstance cube = cubeService.getCubeManager().getCube(cubeName); + if (cube == null) { + throw new InternalErrorException("Cannot find cube " + cubeName); + } + CubeDesc cubeDesc = cube.getDescriptor(); + CubeDesc newCubeDesc = CubeDesc.getCopyOf(cubeDesc); + + newCubeDesc.setName(newCubeName); + + CubeInstance newCube = null; + try { + newCube = cubeService.createCubeAndDesc(newCubeName, project, newCubeDesc); + + //reload to avoid shallow clone + cubeService.getCubeDescManager().reloadCubeDescLocal(newCubeName); + } catch (IOException e) { + throw new InternalErrorException("Failed to clone cube ", e); + } + + boolean isStreamingCube = false, cloneStreamingConfigSuccess = false, cloneKafkaConfigSuccess = false; + + + List<StreamingConfig> streamingConfigs = null; + try { + streamingConfigs = streamingService.listAllStreamingConfigs(cubeName); + if (streamingConfigs.size() != 0) { + isStreamingCube = true; + } + + } catch (IOException e) { + e.printStackTrace(); + } + + StreamingConfig newStreamingConfig = null; + KafkaConfig newKafkaConfig = null; + + try { + + if (isStreamingCube) { + + isStreamingCube = true; + newStreamingConfig = streamingConfigs.get(0).clone(); + newStreamingConfig.setName(newCubeName + "_STREAMING"); + newStreamingConfig.updateRandomUuid(); + newStreamingConfig.setLastModified(0); + newStreamingConfig.setCubeName(newCubeName); + try { + streamingService.createStreamingConfig(newStreamingConfig); + cloneStreamingConfigSuccess = true; + } catch (IOException e) { + throw new InternalErrorException("Failed to clone streaming config. ", e); + } + + //StreamingConfig name and KafkaConfig name is the same for same cube + String kafkaConfigName = streamingConfigs.get(0).getName(); + KafkaConfig kafkaConfig = null; + try { + kafkaConfig = kafkaConfigService.getKafkaConfig(kafkaConfigName); + if (kafkaConfig != null) { + newKafkaConfig = kafkaConfig.clone(); + newKafkaConfig.setName(newStreamingConfig.getName()); + newKafkaConfig.setLastModified(0); + newKafkaConfig.updateRandomUuid(); + } + } catch (IOException e) { + throw new InternalErrorException("Failed to get kafka config info. ", e); + } + + try { + kafkaConfigService.createKafkaConfig(newKafkaConfig); + cloneKafkaConfigSuccess = true; + } catch (IOException e) { + throw new InternalErrorException("Failed to clone streaming config. ", e); + } + } + } finally { + + //rollback if failed + if (isStreamingCube) { + if (cloneStreamingConfigSuccess == false || cloneKafkaConfigSuccess == false) { + try { + cubeService.deleteCube(newCube); + } catch (Exception ex) { + throw new InternalErrorException("Failed, and failed to rollback on delete cube. " + " Caused by: " + ex.getMessage(), ex); + } + if (cloneStreamingConfigSuccess == true) { + try { + streamingService.dropStreamingConfig(newStreamingConfig); + } catch (IOException e) { + throw new InternalErrorException("Failed to clone cube, and StreamingConfig created and failed to delete: " + e.getLocalizedMessage()); + } + } + if (cloneKafkaConfigSuccess == true) { + try { + kafkaConfigService.dropKafkaConfig(newKafkaConfig); + } catch (IOException e) { + throw new InternalErrorException("Failed to clone cube, and KafkaConfig created and failed to delete: " + e.getLocalizedMessage()); + } + } + + } + + } + } + + return newCube; + + } + + @RequestMapping(value = "/{cubeName}/enable", method = {RequestMethod.PUT}) @ResponseBody public CubeInstance enableCube(@PathVariable String cubeName) { @@ -346,7 +463,7 @@ public class CubeController extends BasicController { throw new InternalErrorException(e.getLocalizedMessage(), e); } - boolean createStreamingConfigSuccess = false,createKafkaConfigSuccess = false; + boolean createStreamingConfigSuccess = false, createKafkaConfigSuccess = false; StreamingConfig streamingConfig = null; KafkaConfig kafkaConfig = null; @@ -361,9 +478,10 @@ public class CubeController extends BasicController { cubeRequest.setMessage("No KafkaConfig info defined."); return cubeRequest; } - if(streamingConfig == null){ + if (streamingConfig == null) { cubeRequest.setMessage("No StreamingConfig info defined."); - return cubeRequest; } + return cubeRequest; + } try { streamingConfig.setUuid(UUID.randomUUID().toString()); @@ -383,23 +501,23 @@ public class CubeController extends BasicController { } } - }finally { + } finally { //rollback if failed if (isStreamingCube) { - if(createStreamingConfigSuccess == false || createKafkaConfigSuccess == false){ + if (createStreamingConfigSuccess == false || createKafkaConfigSuccess == false) { try { cubeService.deleteCube(cubeInstance); } catch (Exception ex) { throw new InternalErrorException("Failed to rollback on delete cube. " + " Caused by: " + ex.getMessage(), ex); } - if(createStreamingConfigSuccess == true){ + if (createStreamingConfigSuccess == true) { try { streamingService.dropStreamingConfig(streamingConfig); } catch (IOException e) { throw new InternalErrorException("Failed to create cube, and StreamingConfig created and failed to delete: " + e.getLocalizedMessage()); } } - if(createKafkaConfigSuccess == true){ + if (createKafkaConfigSuccess == true) { try { kafkaConfigService.dropKafkaConfig(kafkaConfig); } catch (IOException e) { @@ -469,14 +587,14 @@ public class CubeController extends BasicController { return cubeRequest; } - boolean updateStreamingConfigSuccess = false,updateKafkaConfigSuccess = false; + boolean updateStreamingConfigSuccess = false, updateKafkaConfigSuccess = false; boolean isStreamingCube = cubeRequest.getStreamingCube() != null && cubeRequest.getStreamingCube().equals("true"); //oldConfig is for recover use - StreamingConfig streamingConfig = null,oldStreamingConfig =null; - KafkaConfig kafkaConfig = null,oldKafkaConfig = null; - if(isStreamingCube){ + StreamingConfig streamingConfig = null, oldStreamingConfig = null; + KafkaConfig kafkaConfig = null, oldKafkaConfig = null; + if (isStreamingCube) { streamingConfig = deserializeStreamingDesc(cubeRequest); kafkaConfig = deserializeKafkaDesc(cubeRequest); try { @@ -498,7 +616,7 @@ public class CubeController extends BasicController { return cubeRequest; } - if(oldStreamingConfig == null){ + if (oldStreamingConfig == null) { streamingConfig.setUuid(UUID.randomUUID().toString()); try { streamingService.createStreamingConfig(streamingConfig); @@ -507,7 +625,7 @@ public class CubeController extends BasicController { logger.error("Failed to add StreamingConfig:" + e.getLocalizedMessage(), e); throw new InternalErrorException("Failed to add StreamingConfig: " + e.getLocalizedMessage()); } - }else{ + } else { try { streamingConfig = streamingService.updateStreamingConfig(streamingConfig); updateStreamingConfigSuccess = true; @@ -517,7 +635,7 @@ public class CubeController extends BasicController { throw new InternalErrorException("Failed to update StreamingConfig: " + e.getLocalizedMessage()); } } - if(oldKafkaConfig == null){ + if (oldKafkaConfig == null) { kafkaConfig.setUuid(UUID.randomUUID().toString()); try { kafkaConfigService.createKafkaConfig(kafkaConfig); @@ -527,7 +645,7 @@ public class CubeController extends BasicController { throw new InternalErrorException("Failed to add KafkaConfig: " + e.getLocalizedMessage()); } - }else{ + } else { try { kafkaConfig = kafkaConfigService.updateKafkaConfig(kafkaConfig); updateKafkaConfigSuccess = true; @@ -538,10 +656,10 @@ public class CubeController extends BasicController { } } - }finally { + } finally { if (isStreamingCube) { //recover cube desc - if(updateStreamingConfigSuccess == false || updateKafkaConfigSuccess ==false){ + if (updateStreamingConfigSuccess == false || updateKafkaConfigSuccess == false) { oldCubeDesc.setLastModified(desc.getLastModified()); CubeInstance cube = cubeService.getCubeManager().getCube(cubeRequest.getCubeName()); try { @@ -551,9 +669,9 @@ public class CubeController extends BasicController { throw new InternalErrorException("Failed to recover CubeDesc: " + e.getLocalizedMessage()); } - if(updateStreamingConfigSuccess == true){ + if (updateStreamingConfigSuccess == true) { - if(oldStreamingConfig!=null){ + if (oldStreamingConfig != null) { oldStreamingConfig.setLastModified(streamingConfig.getLastModified()); try { @@ -562,7 +680,7 @@ public class CubeController extends BasicController { logger.error("Failed to recover StreamingConfig:" + e.getLocalizedMessage(), e); throw new InternalErrorException("Failed to recover StreamingConfig: " + e.getLocalizedMessage()); } - } else{ + } else { try { streamingService.dropStreamingConfig(streamingConfig); } catch (IOException e) { @@ -572,8 +690,8 @@ public class CubeController extends BasicController { } } - if(updateKafkaConfigSuccess == true){ - if(oldKafkaConfig!=null) { + if (updateKafkaConfigSuccess == true) { + if (oldKafkaConfig != null) { oldKafkaConfig.setLastModified(kafkaConfig.getLastModified()); try { kafkaConfigService.updateKafkaConfig(oldKafkaConfig); @@ -581,7 +699,7 @@ public class CubeController extends BasicController { logger.error("Failed to recover KafkaConfig:" + e.getLocalizedMessage(), e); throw new InternalErrorException("Failed to recover KafkaConfig: " + e.getLocalizedMessage()); } - }else{ + } else { try { kafkaConfigService.dropKafkaConfig(kafkaConfig); } catch (IOException e) { @@ -780,4 +898,5 @@ public class CubeController extends BasicController { public void setKafkaConfigService(KafkaConfigService kafkaConfigService) { this.kafkaConfigService = kafkaConfigService; } + } http://git-wip-us.apache.org/repos/asf/kylin/blob/192bbf91/server/src/main/java/org/apache/kylin/rest/controller/ModelController.java ---------------------------------------------------------------------- diff --git a/server/src/main/java/org/apache/kylin/rest/controller/ModelController.java b/server/src/main/java/org/apache/kylin/rest/controller/ModelController.java index a409938..00b79b0 100644 --- a/server/src/main/java/org/apache/kylin/rest/controller/ModelController.java +++ b/server/src/main/java/org/apache/kylin/rest/controller/ModelController.java @@ -18,13 +18,15 @@ package org.apache.kylin.rest.controller; -import java.io.IOException; +import java.io.*; import java.util.Iterator; import java.util.List; import java.util.UUID; import org.apache.commons.lang.StringUtils; +import org.apache.kylin.common.KylinConfig; import org.apache.kylin.common.util.JsonUtil; +import org.apache.kylin.metadata.MetadataManager; import org.apache.kylin.metadata.model.DataModelDesc; import org.apache.kylin.metadata.project.ProjectInstance; import org.apache.kylin.rest.exception.BadRequestException; @@ -149,6 +151,47 @@ public class ModelController extends BasicController { } } + @RequestMapping(value = "/{modelName}/clone", method = {RequestMethod.PUT}) + @ResponseBody + public ModelRequest cloneModel(@PathVariable String modelName, @RequestBody ModelRequest modelRequest) { + String project = modelRequest.getProject(); + MetadataManager metaManager = MetadataManager.getInstance(KylinConfig.getInstanceFromEnv()); + DataModelDesc modelDesc = metaManager.getDataModelDesc(modelName); + String newModelName = modelRequest.getModelName(); + + if (StringUtils.isEmpty(project)) { + logger.info("Project name should not be empty."); + throw new BadRequestException("Project name should not be empty."); + } + + if (modelDesc == null || StringUtils.isEmpty(modelName)) { + logger.info("Model does not exist."); + throw new BadRequestException("Model does not exist."); + } + + if (StringUtils.isEmpty(newModelName)) { + logger.info("New model name is empty."); + throw new BadRequestException("New model name is empty."); + } + + + DataModelDesc newModelDesc = DataModelDesc.getCopyOf(modelDesc); + newModelDesc.setName(newModelName); + try { + newModelDesc = modelService.createModelDesc(project, newModelDesc); + + //reload avoid shallow + metaManager.reloadDataModelDesc(newModelName); + } catch (IOException e) { + throw new InternalErrorException("failed to clone DataModelDesc", e); + } + + modelRequest.setUuid(newModelDesc.getUuid()); + modelRequest.setSuccessful(true); + return modelRequest; + } + + private DataModelDesc deserializeDataModelDesc(ModelRequest modelRequest) { DataModelDesc desc = null; try { @@ -190,4 +233,5 @@ public class ModelController extends BasicController { } return buffer.toString(); } + } http://git-wip-us.apache.org/repos/asf/kylin/blob/192bbf91/webapp/app/js/controllers/cubes.js ---------------------------------------------------------------------- diff --git a/webapp/app/js/controllers/cubes.js b/webapp/app/js/controllers/cubes.js index b9a0f13..7ea626a 100644 --- a/webapp/app/js/controllers/cubes.js +++ b/webapp/app/js/controllers/cubes.js @@ -377,6 +377,22 @@ KylinApp }; + $scope.cloneCube = function(cube){ + $scope.loadDetail(cube).then(function () { + $modal.open({ + templateUrl: 'cubeClone.html', + controller: cubeCloneCtrl, + windowClass:"clone-cube-window", + resolve: { + cube: function () { + return cube; + } + } + }); + }); + } + + $scope.cubeEdit = function (cube) { $location.path("cubes/edit/" + cube.name); } @@ -402,6 +418,59 @@ KylinApp } }); + +var cubeCloneCtrl = function ($scope, $modalInstance, CubeService, MessageService, $location, cube, MetaModel, SweetAlert,ProjectModel, loadingRequest) { + $scope.projectModel = ProjectModel; + + $scope.targetObj={ + cubeName:cube.descriptor+"_clone", + targetProject:$scope.projectModel.selectedProject + } + + $scope.cancel = function () { + $modalInstance.dismiss('cancel'); + }; + + $scope.cloneCube = function(){ + + $scope.cubeRequest = { + cubeName:$scope.targetObj.cubeName, + project:$scope.targetObj.targetProject + } + + SweetAlert.swal({ + title: '', + text: 'Are you sure to clone the cube? ', + type: '', + showCancelButton: true, + confirmButtonColor: '#DD6B55', + confirmButtonText: "Yes", + closeOnConfirm: true + }, function (isConfirm) { + if (isConfirm) { + + loadingRequest.show(); + CubeService.clone({cubeId: cube.name}, $scope.cubeRequest, function (result) { + loadingRequest.hide(); + SweetAlert.swal('Success!', 'Clone cube successfully', 'success'); + location.reload(); + }, function (e) { + loadingRequest.hide(); + 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'); + } + }); + } + }); + } + +} + + var jobSubmitCtrl = function ($scope, $modalInstance, CubeService, MessageService, $location, cube, metaModel, buildType, SweetAlert, loadingRequest) { $scope.cube = cube; $scope.metaModel = metaModel; http://git-wip-us.apache.org/repos/asf/kylin/blob/192bbf91/webapp/app/js/controllers/models.js ---------------------------------------------------------------------- diff --git a/webapp/app/js/controllers/models.js b/webapp/app/js/controllers/models.js index 63c2683..e32ccf3 100644 --- a/webapp/app/js/controllers/models.js +++ b/webapp/app/js/controllers/models.js @@ -122,6 +122,21 @@ KylinApp.controller('ModelsCtrl', function ($scope, $q, $routeParams, $location, }); }; + $scope.cloneModel = function(model){ + $modal.open({ + templateUrl: 'modelClone.html', + controller: modelCloneCtrl, + windowClass:"clone-cube-window", + resolve: { + model: function () { + return model; + } + } + }); + } + + + $scope.openModal = function (model) { $scope.modelsManager.selectedModel = model; $modal.open({ @@ -142,3 +157,55 @@ KylinApp.controller('ModelsCtrl', function ($scope, $q, $routeParams, $location, }; }); + + +var modelCloneCtrl = function ($scope, $modalInstance, CubeService, MessageService, $location, model, MetaModel, SweetAlert,ProjectModel, loadingRequest,ModelService) { + $scope.projectModel = ProjectModel; + + $scope.targetObj={ + modelName:model.name+"_clone", + targetProject:$scope.projectModel.selectedProject + } + + $scope.cancel = function () { + $modalInstance.dismiss('cancel'); + }; + + $scope.cloneModel = function(){ + + $scope.modelRequest = { + modelName:$scope.targetObj.modelName, + project:$scope.targetObj.targetProject + } + + SweetAlert.swal({ + title: '', + text: 'Are you sure to clone the model? ', + type: '', + showCancelButton: true, + confirmButtonColor: '#DD6B55', + confirmButtonText: "Yes", + closeOnConfirm: true + }, function (isConfirm) { + if (isConfirm) { + + loadingRequest.show(); + ModelService.clone({modelId: model.name}, $scope.modelRequest, function (result) { + loadingRequest.hide(); + SweetAlert.swal('Success!', 'Clone model successfully', 'success'); + location.reload(); + }, function (e) { + loadingRequest.hide(); + 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'); + } + }); + } + }); + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/192bbf91/webapp/app/js/services/cubes.js ---------------------------------------------------------------------- diff --git a/webapp/app/js/services/cubes.js b/webapp/app/js/services/cubes.js index 3df6dd0..2e7a185 100644 --- a/webapp/app/js/services/cubes.js +++ b/webapp/app/js/services/cubes.js @@ -28,6 +28,7 @@ KylinApp.factory('CubeService', ['$resource', function ($resource, config) { disable: {method: 'PUT', params: {action: 'disable'}, isArray: false}, enable: {method: 'PUT', params: {action: 'enable'}, isArray: false}, purge: {method: 'PUT', params: {action: 'purge'}, isArray: false}, + clone: {method: 'PUT', params: {action: 'clone'}, isArray: false}, drop: {method: 'DELETE', params: {}, isArray: false}, save: {method: 'POST', params: {}, isArray: false}, update: {method: 'PUT', params: {}, isArray: false}, http://git-wip-us.apache.org/repos/asf/kylin/blob/192bbf91/webapp/app/js/services/models.js ---------------------------------------------------------------------- diff --git a/webapp/app/js/services/models.js b/webapp/app/js/services/models.js index 76af969..ae19dd4 100644 --- a/webapp/app/js/services/models.js +++ b/webapp/app/js/services/models.js @@ -21,6 +21,7 @@ KylinApp.factory('ModelService', ['$resource', function ($resource, config) { list: {method: 'GET', params: {}, isArray: true}, 'get': {method: 'GET',isArray:true}, drop: {method: 'DELETE', params: {}, isArray: false}, + clone: {method: 'PUT', params: {action: 'clone'}, isArray: false}, save: {method: 'POST', params: {}, isArray: false}, update: {method: 'PUT', params: {}, isArray: false} }); http://git-wip-us.apache.org/repos/asf/kylin/blob/192bbf91/webapp/app/less/app.less ---------------------------------------------------------------------- diff --git a/webapp/app/less/app.less b/webapp/app/less/app.less index c42b7b9..3884e8f 100644 --- a/webapp/app/less/app.less +++ b/webapp/app/less/app.less @@ -672,3 +672,11 @@ ul.messenger .messenger-message-inner,.ngCellText { .progress.progress-striped { background-color:#DCDCDC; } + +.clone-cube-window .modal-dialog { + width: 600px; /* desired relative width */ +} + +.tooltip { + z-index:1240000 !important; +} http://git-wip-us.apache.org/repos/asf/kylin/blob/192bbf91/webapp/app/partials/cubes/cube_clone.html ---------------------------------------------------------------------- diff --git a/webapp/app/partials/cubes/cube_clone.html b/webapp/app/partials/cubes/cube_clone.html new file mode 100644 index 0000000..edbc137 --- /dev/null +++ b/webapp/app/partials/cubes/cube_clone.html @@ -0,0 +1,63 @@ +<!-- +* 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. +--> + +<script type="text/ng-template" id="cubeClone.html"> + <div class="modal-header"> + <h4 tooltip="submit">CUBE CLONE</h4> + </div> + <div class="modal-body" style="background-color: white"> + + <div class="row"> + <div class="col-md-2"></div> + <div class="col-md-8"> + <div class="row"> + <div class="form-group"> + <b>Target Project is:</b> + <br/> + <select ng-required="projectModel.projects.length" chosen ng-model="targetObj.targetProject" + ng-init="newAccess.permission=permissions.READ.value;" + ng-options="project.name as project.name for project in projectModel.projects " + style="width: 100% !important;" + data-placeholder="select a project" + class="chosen-select"> + <option value=""></option> + </select> + </div> + </div> + </div> + <div class="col-md-2"></div> + </div> + <div class="row"> + <div class="col-md-2"></div> + <div class="col-md-8"> + <div class="row"> + <div class="form-group"> + <b>New Cube Name:</b> + <br/> + <input type="text" class="form-control" ng-model="targetObj.cubeName"/> + </div> + </div> + </div> + <div class="col-md-2"></div> + </div> + </div> + <div class="modal-footer"> + <button class="btn btn-success" ng-click="cloneCube()">Submit</button> + <button class="btn btn-primary" ng-click="cancel()">Close</button> + </div> +</script> http://git-wip-us.apache.org/repos/asf/kylin/blob/192bbf91/webapp/app/partials/cubes/cubes.html ---------------------------------------------------------------------- diff --git a/webapp/app/partials/cubes/cubes.html b/webapp/app/partials/cubes/cubes.html index 2b456f4..a1f5e1c 100644 --- a/webapp/app/partials/cubes/cubes.html +++ b/webapp/app/partials/cubes/cubes.html @@ -91,6 +91,7 @@ <li ng-if="cube.status!='DISABLED'"><a ng-click="disable(cube)">Disable</a></li> <li ng-if="cube.status=='DISABLED'"><a ng-click="enable(cube)">Enable</a></li> <li ng-if="cube.status=='DISABLED'"><a ng-click="purge(cube)">Purge</a></li> + <li><a ng-click="cloneCube(cube)">Clone</a></li> </ul> </div> @@ -138,5 +139,6 @@ <div ng-include="'partials/jobs/job_merge.html'"></div> <div ng-include="'partials/projects/project_create.html'"></div> <div ng-include="'partials/models/model_detail.html'"></div> +<div ng-include="'partials/cubes/cube_clone.html'"></div> </div> http://git-wip-us.apache.org/repos/asf/kylin/blob/192bbf91/webapp/app/partials/models/model_clone.html ---------------------------------------------------------------------- diff --git a/webapp/app/partials/models/model_clone.html b/webapp/app/partials/models/model_clone.html new file mode 100644 index 0000000..de1b562 --- /dev/null +++ b/webapp/app/partials/models/model_clone.html @@ -0,0 +1,63 @@ +<!-- +* 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. +--> + +<script type="text/ng-template" id="modelClone.html"> + <div class="modal-header"> + <h4 tooltip="submit">MODEL CLONE</h4> + </div> + <div class="modal-body" style="background-color: white"> + + <div class="row"> + <div class="col-md-2"></div> + <div class="col-md-8"> + <div class="row"> + <div class="form-group"> + <b>Target Project is:</b> + <br/> + <select ng-required="projectModel.projects.length" chosen ng-model="targetObj.targetProject" + ng-init="newAccess.permission=permissions.READ.value;" + ng-options="project.name as project.name for project in projectModel.projects " + style="width: 100% !important;" + data-placeholder="select a project" + class="chosen-select"> + <option value=""></option> + </select> + </div> + </div> + </div> + <div class="col-md-2"></div> + </div> + <div class="row"> + <div class="col-md-2"></div> + <div class="col-md-8"> + <div class="row"> + <div class="form-group"> + <b>New Model Name:</b> + <br/> + <input type="text" class="form-control" ng-model="targetObj.modelName"/> + </div> + </div> + </div> + <div class="col-md-2"></div> + </div> + </div> + <div class="modal-footer"> + <button class="btn btn-success" ng-click="cloneModel()">Submit</button> + <button class="btn btn-primary" ng-click="cancel()">Close</button> + </div> +</script> http://git-wip-us.apache.org/repos/asf/kylin/blob/192bbf91/webapp/app/partials/models/models.html ---------------------------------------------------------------------- diff --git a/webapp/app/partials/models/models.html b/webapp/app/partials/models/models.html index 36b4d54..4d8c7ac 100644 --- a/webapp/app/partials/models/models.html +++ b/webapp/app/partials/models/models.html @@ -63,3 +63,4 @@ <div ng-include="'partials/jobs/job_merge.html'"></div> <div ng-include="'partials/projects/project_create.html'"></div> <div ng-include="'partials/models/model_detail.html'"></div> +<div ng-include="'partials/models/model_clone.html'"></div> http://git-wip-us.apache.org/repos/asf/kylin/blob/192bbf91/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 c7328a6..3e4a6d3 100644 --- a/webapp/app/partials/models/models_tree.html +++ b/webapp/app/partials/models/models_tree.html @@ -41,7 +41,7 @@ <div> <h3 class="text-info">Models</h3> </div> - <div style="width:100%; height:{{window}}px; overflow:auto;margin-top: 20px;"> + <div style="width:100%; height:{{window}}px; overflow:auto;margin-top: 20px;" class="cube_model_trees"> <ul class="list-group models-tree"> <li class="list-group-item" ng-repeat="model in modelsManager.models"> @@ -51,8 +51,9 @@ <div class="pull-right" showonhoverparent style="display:none;"> - <a href="models/edit/{{model.name}}" ng-if="userService.hasRole('ROLE_MODELER')"><span class="fa fa-pencil fa-lg fa-fw"></span></a> - <a ng-click="dropModel(model)" style="cursor:pointer;margin-right: 8px;" ng-if="userService.hasRole('ROLE_MODELER')"><span class="fa fa-trash-o fa-lg fa-fw"></span></a> + <a href="models/edit/{{model.name}}" data-placement="bottom" title="Edit Model" ng-if="userService.hasRole('ROLE_MODELER')"><span class="fa fa-pencil fa-lg fa-fw"></span></a> + <a ng-click="cloneModel(model)" title="Clone Model" style="cursor:pointer;margin-right: 8px;" ng-if="userService.hasRole('ROLE_MODELER')"><span class="fa fa-copy fa-lg fa-fw"></span></a> + <a ng-click="dropModel(model)" title="Drop Model" style="cursor:pointer;margin-right: 8px;" ng-if="userService.hasRole('ROLE_MODELER')"><span class="fa fa-trash-o fa-lg fa-fw"></span></a> </div> </li>