blob: b77b5b89e4c0f9158e2f3ae6f868d4ac45b9bfeb [file] [log] [blame]
/*
* 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.
*/
'use strict';
KylinApp.controller('CubeSchemaCtrl', function ($scope, QueryService, UserService,modelsManager, ProjectService, AuthenticationService,$filter,ModelService,MetaModel,CubeDescModel,CubeList,TableModel,ProjectModel,ModelDescService,SweetAlert,cubesManager,StreamingService,CubeService,VdmUtil,tableConfig) {
$scope.modelsManager = modelsManager;
$scope.cubesManager = cubesManager;
$scope.projects = [];
$scope.newDimension = null;
$scope.newMeasure = null;
$scope.forms={};
$scope.wizardSteps = [
{title: 'Cube Info', src: 'partials/cubeDesigner/info.html', isComplete: false,form:'cube_info_form'},
{title: 'Dimensions', src: 'partials/cubeDesigner/dimensions.html', isComplete: false,form:'cube_dimension_form'},
{title: 'Measures', src: 'partials/cubeDesigner/measures.html', isComplete: false,form:'cube_measure_form'},
{title: 'Refresh Setting', src: 'partials/cubeDesigner/refresh_settings.html', isComplete: false,form:'refresh_setting_form'},
{title: 'Advanced Setting', src: 'partials/cubeDesigner/advanced_settings.html', isComplete: false,form:'cube_setting_form'},
{title: 'Configuration Overwrites ', src: 'partials/cubeDesigner/cubeOverwriteProp.html', isComplete: false,form:'cube_overwrite_prop_form'},
{title: 'Overview', src: 'partials/cubeDesigner/overview.html', isComplete: false,form:null}
];
$scope.curStep = $scope.wizardSteps[0];
$scope.findMeasuresStepIndex = function() {
for (var i = 0; i < $scope.wizardSteps.length; i++) {
if ($scope.wizardSteps[i].title === 'Measures') {
return i;
}
}
return 0;
}
if ($scope.isMeasureEdit) {
var editMeasureStepIdx = $scope.findMeasuresStepIndex();
$scope.curStep = $scope.wizardSteps[editMeasureStepIdx];
}
$scope.getTypeVersion=function(typename){
var searchResult=/\[v(\d+)\]/.exec(typename);
if(searchResult&&searchResult.length){
return searchResult.length&&searchResult[1]||1;
}else{
return 1;
}
}
$scope.removeVersion=function(typename){
if(typename){
return typename.replace(/\[v\d+\]/g,"").replace(/\s+/g,'');
}
return "";
}
//init encoding list
$scope.store = {
supportedEncoding:[],
encodingMaps:{}
}
TableModel.getColumnTypeEncodingMap().then(function(data){
$scope.store.encodingMaps=data;
});
CubeService.getValidEncodings({}, function (encodings) {
if(encodings){
for(var i in encodings)
if(VdmUtil.isNotExtraKey(encodings,i)){
var value = i
var name = value;
var typeVersion=+encodings[i]||1;
var suggest=false,selecttips='';
if(/\d+/.test(""+typeVersion)&&typeVersion>=1){
for(var s=1;s<=typeVersion;s++){
if(s==typeVersion){
suggest=true;
}
if(value=="int"){
name = "int (deprecated)";
suggest=false;
}
if(typeVersion>1){
selecttips=" (v"+s;
if(s==typeVersion){
selecttips+=",suggest"
}
selecttips+=')';
}
$scope.store.supportedEncoding.push({
"name":name+selecttips,
"value":value+"[v"+s+"]",
"version":typeVersion,
"baseValue":value,
"suggest":suggest
});
}
}
}
}
},function(e){
$scope.store.supportedEncoding = $scope.cubeConfig.encodings;
})
$scope.getEncodings =function (name){
var filterName=name;
var columnType= $scope.modelsManager.getColumnTypeByColumnName(filterName);
var matchList=VdmUtil.getObjValFromLikeKey($scope.store.encodingMaps,columnType);
var encodings =$scope.store.supportedEncoding,filterEncoding;
if($scope.isEdit){
var rowkey_columns=$scope.cubeMetaFrame.rowkey.rowkey_columns;
if(rowkey_columns&&filterName){
for(var s=0;s<rowkey_columns.length;s++){
if(filterName==rowkey_columns[s].column){
var version=rowkey_columns[s].encoding_version;
var noLenEncoding=rowkey_columns[s].encoding.replace(/:\d+/,"");
filterEncoding=VdmUtil.getFilterObjectListByOrFilterVal(encodings,'value',noLenEncoding+(version?"[v"+version+"]":"[v1]"),'suggest',true)
matchList.push(noLenEncoding);
filterEncoding=VdmUtil.getObjectList(filterEncoding,'baseValue',matchList);
break;
}
}
}else{
filterEncoding=VdmUtil.getFilterObjectListByOrFilterVal(encodings,'suggest',true);
filterEncoding=VdmUtil.getObjectList(filterEncoding,'baseValue',matchList)
}
}else{
filterEncoding=VdmUtil.getFilterObjectListByOrFilterVal(encodings,'suggest',true);
filterEncoding=VdmUtil.getObjectList(filterEncoding,'baseValue',matchList)
}
return filterEncoding;
}
$scope.allCubes = [];
$scope.getTypeVersion=function(typename){
var searchResult=/\[v(\d+)\]/.exec(typename);
if(searchResult&&searchResult.length){
return searchResult.length&&searchResult[1]||1;
}else{
return 1;
}
}
$scope.removeVersion=function(typename){
if(typename){
return typename.replace(/\[v\d+\]/g,"");
}
return "";
}
// ~ init
if (!$scope.state) {
$scope.state = {mode: "view"};
}
$scope.$watch('cubeMetaFrame', function (newValue, oldValue) {
if(!newValue){
return;
}
if ($scope.cubeMode=="editExistCube"&&newValue && !newValue.project) {
initProject();
}
});
var queryParam = {offset: 0, limit: 65535};
CubeService.list(queryParam, function (all_cubes) {
if($scope.allCubes.length > 0){
$scope.allCubes.splice(0,$scope.allCubes.length);
}
for (var i = 0; i < all_cubes.length; i++) {
$scope.allCubes.push(all_cubes[i].name.toUpperCase());
}
});
// ~ public methods
$scope.filterProj = function(project){
return $scope.userService.hasRole('ROLE_ADMIN') || $scope.hasPermission(project,$scope.permissions.ADMINISTRATION.mask);
};
$scope.removeElement = function (arr, element) {
var index = arr.indexOf(element);
if (index > -1) {
arr.splice(index, 1);
}
};
$scope.open = function ($event) {
$event.preventDefault();
$event.stopPropagation();
$scope.opened = true;
};
$scope.preView = function () {
var stepIndex = $scope.wizardSteps.indexOf($scope.curStep);
if (stepIndex >= 1) {
$scope.curStep.isComplete = false;
$scope.curStep = $scope.wizardSteps[stepIndex - 1];
}
};
$scope.nextView = function () {
var stepIndex = $scope.wizardSteps.indexOf($scope.curStep);
if (stepIndex < ($scope.wizardSteps.length - 1)) {
$scope.curStep.isComplete = true;
$scope.curStep = $scope.wizardSteps[stepIndex + 1];
AuthenticationService.ping(function (data) {
UserService.setCurUser(data);
});
}
};
$scope.goToStep = function(stepIndex){
if($scope.cubeMode == "addNewCube"){
if(stepIndex+1>=$scope.curStep.step){
return;
}
}
for(var i=0;i<$scope.wizardSteps.length;i++){
if(i<=stepIndex){
$scope.wizardSteps[i].isComplete = true;
}else{
$scope.wizardSteps[i].isComplete = false;
}
}
if (stepIndex < ($scope.wizardSteps.length)) {
$scope.curStep = $scope.wizardSteps[stepIndex];
AuthenticationService.ping(function (data) {
UserService.setCurUser(data);
});
}
}
$scope.$watch('cube.detail', function (newValue, oldValue) {
if (!newValue) {
return;
}
if (newValue && $scope.state.mode === "view") {
$scope.cubeMetaFrame = newValue;
// when viw state,each cubeSchema has its own metaModel
$scope.metaModel = {
model: {}
}
$scope.metaModel.model=modelsManager.getModel($scope.cubeMetaFrame.model_name);
}
});
$scope.$watch('cubeMetaFrame', function (newValue, oldValue) {
if (!newValue) {
return;
}
if ($scope.cubeMode == "editExistCube" && newValue && !newValue.project) {
initProject();
}
});
$scope.checkCubeForm = function(stepIndex){
// do not check for Prev Step
if (stepIndex + 1 < $scope.curStep.step) {
return true;
}
if(!$scope.curStep.form){
return true;
}
if($scope.state.mode==='view'){
return true;
}
else{
//form validation
if($scope.forms[$scope.curStep.form].$invalid){
$scope.forms[$scope.curStep.form].$submitted = true;
return false;
}else{
//business rule check
switch($scope.curStep.form){
case 'cube_info_form':
return $scope.check_cube_info();
break;
case 'cube_dimension_form':
return $scope.check_cube_dimension();
break;
case 'cube_measure_form':
return $scope.check_cube_measure();
break;
case 'cube_setting_form':
return $scope.check_cube_setting();
case 'cube_overwrite_prop_form':
return $scope.cube_overwrite_prop_check();
default:
return true;
break;
}
}
}
};
$scope.check_cube_info = function(){
if(($scope.state.mode === "edit") &&$scope.cubeMode=="addNewCube"&&($scope.allCubes.indexOf($scope.cubeMetaFrame.name.toUpperCase()) >= 0)){
SweetAlert.swal('Oops...', "The cube named [" + $scope.cubeMetaFrame.name.toUpperCase() + "] already exists", 'warning');
return false;
}
// Update storage type according to the streaming table in model
if(TableModel.selectProjectTables.some(function(table) {
return (table.name === $scope.metaModel.model.fact_table && _.values(tableConfig.streamingSourceType).indexOf(table.source_type) > -1)
})) {
$scope.cubeMetaFrame.storage_type = 3;
} else {
$scope.cubeMetaFrame.storage_type = 2;
}
return true;
}
$scope.check_cube_dimension = function(){
var errors = [];
if(!$scope.cubeMetaFrame.dimensions.length){
errors.push("Dimension can't be null");
}
var errorInfo = "";
angular.forEach(errors,function(item){
errorInfo+="\n"+item;
});
if(errors.length){
SweetAlert.swal('', errorInfo, 'warning');
return false;
}else{
return true;
}
};
$scope.check_cube_measure = function(){
var _measures = $scope.cubeMetaFrame.measures;
var errors = [];
if(!_measures||!_measures.length){
errors.push("Please define your metrics.");
}
var existCountExpression = false;
for(var i=0;i<_measures.length;i++){
if(_measures[i].function.expression=="COUNT"){
existCountExpression=true;
break;
}
}
if(!existCountExpression){
errors.push("[COUNT] metric is required.");
}
var errorInfo = "";
angular.forEach(errors,function(item){
errorInfo+="\n"+item;
});
if(errors.length){
SweetAlert.swal('', errorInfo, 'warning');
return false;
}else{
return true;
}
}
$scope.check_cube_setting = function(){
var errors = [];
angular.forEach($scope.cubeMetaFrame.aggregation_groups,function(group){
if(!group&&!group.includes){
errors.push("Each aggregation group can't be empty.");
}
})
var shardRowkeyList = [];
angular.forEach($scope.cubeMetaFrame.rowkey.rowkey_columns,function(rowkey){
if(rowkey.isShardBy == true){
shardRowkeyList.push(rowkey.column);
}
if(rowkey.encoding.substr(0,3)=='int' && (rowkey.encoding.substr(4)<1 || rowkey.encoding.substr(4)>8)){
errors.push("int encoding column length should between 1 and 8.");
}
})
if(shardRowkeyList.length >1){
errors.push("At most one 'shard by' column is allowed.");
}
var cfMeasures = [];
angular.forEach($scope.cubeMetaFrame.hbase_mapping.column_family, function (colFamily, index) {
angular.forEach(colFamily.columns, function (column, index) {
angular.forEach(column.measure_refs, function(measure) {
cfMeasures.push(measure);
});
});
});
var uniqCfMeasures = _.uniq(cfMeasures);
if(uniqCfMeasures.length != $scope.cubeMetaFrame.measures.length) {
errors.push("All measures need to be assigned to column family");
}
var isCFEmpty = _.some($scope.cubeMetaFrame.hbase_mapping.column_family, function(colFamily) {
return colFamily.columns[0].measure_refs.length == 0;
});
if (isCFEmpty == true) {
errors.push("Each column family can't not be empty");
}
angular.forEach($scope.cubeMetaFrame.measures, function (measure, index) {
if (measure.function.expression === 'COUNT_DISTINCT' && measure.function.returntype === 'bitmap' && !$scope.isIntMeasure(measure)) {
var measureColumn = measure.function.parameter.value;
var isColumnExit = false;
angular.forEach($scope.cubeMetaFrame.dictionaries, function (dictionaries) {
if (!isColumnExit) {
//keep backward compatibility
if (dictionaries.column == measureColumn || dictionaries.column == VdmUtil.removeNameSpace(measureColumn))
isColumnExit = true;
}
});
if (!isColumnExit) {
errors.push("The non-Int type precise count distinct measure must set advanced cict: " + measureColumn);
}
}
});
var errorInfo = "";
angular.forEach(errors,function(item){
errorInfo+="\n"+item;
});
if(errors.length){
SweetAlert.swal('', errorInfo, 'warning');
return false;
}else{
return true;
}
}
$scope.cube_overwrite_prop_check = function(){
var errors = [];
for(var key in $scope.cubeMetaFrame.override_kylin_properties){
if(key==''){
errors.push("Property name is required.");
}
if($scope.cubeMetaFrame.override_kylin_properties[key] == ''){
errors.push("Property value is required.");
}
}
var errorInfo = "";
angular.forEach(errors,function(item){
errorInfo+="\n"+item;
});
if(errors.length){
SweetAlert.swal('', errorInfo, 'warning');
return false;
}else{
return true;
}
}
// ~ private methods
function initProject() {
ProjectService.listReadable({}, function (projects) {
$scope.projects = projects;
var cubeName = (!!$scope.routeParams.cubeName)? $scope.routeParams.cubeName:$scope.state.cubeName;
if (cubeName) {
var projName = null;
if(ProjectModel.getSelectedProject()){
projName=ProjectModel.getSelectedProject();
}else{
angular.forEach($scope.projects, function (project, index) {
angular.forEach(project.realizations, function (unit, index) {
if (!projName && unit.type=="CUBE"&&unit.realization === cubeName) {
projName = project.name;
}
});
});
}
if(!ProjectModel.getSelectedProject()){
ProjectModel.setSelectedProject(projName);
TableModel.aceSrcTbLoaded();
}
$scope.cubeMetaFrame.project = projName;
}
angular.forEach($scope.projects, function (project, index) {
$scope.listAccess(project, 'ProjectInstance');
});
});
}
$scope.cancel = function () {
$location.path("models");
}
});