You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@falcon.apache.org by pe...@apache.org on 2016/08/22 04:47:59 UTC
[06/16] falcon git commit: FALCON-2118 Proposal for new UI changes
http://git-wip-us.apache.org/repos/asf/falcon/blob/76dc2e18/falcon-ui/app/js/controllers/root-controller.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/root-controller.js b/falcon-ui/app/js/controllers/root-controller.js
index 709f6b2..b5dadaa 100644
--- a/falcon-ui/app/js/controllers/root-controller.js
+++ b/falcon-ui/app/js/controllers/root-controller.js
@@ -22,11 +22,11 @@
app.controller('RootCtrl', [
"$scope", "$timeout", "Falcon", "FileApi", "EntityModel",
- "$state", "X2jsService", "ValidationService", "SpinnersFlag", "EntityFalcon",
+ "$state", "X2jsService", "ValidationService", "SpinnersFlag", "EntityFalcon", '$localStorage',
function ($scope, $timeout, Falcon, FileApi,
- EntityModel, $state, X2jsService, validationService, SpinnersFlag, EntityFalcon) {
+ EntityModel, $state, X2jsService, validationService, SpinnersFlag, EntityFalcon, $localStorage) {
- var resultsPerPage = 10;
+ var resultsPerPage = 20;
$scope.server = Falcon;
$scope.validations = validationService;
@@ -35,28 +35,28 @@
$scope.pages = [];
$scope.nextPages = false;
+ $scope.hasClusters = true;
$scope.handleFile = function (evt) {
Falcon.logRequest();
FileApi.loadFile(evt).then(function () {
if (EntityModel.type === 'Type not recognized') {
- Falcon.logResponse('error', {status: 'ERROR', message:'Invalid xml. File not uploaded'}, false);
+ Falcon.logResponse('error', {status: 'ERROR', message:"Entity type not recognized"}, false);
} else {
- Falcon.postSubmitEntity(FileApi.fileRaw, EntityModel.type).success(function (response) {
- Falcon.logResponse('success', response, false);
- $scope.refreshList($scope.tags);
- }).error(function (err) {
- Falcon.logResponse('error', err, false);
- });
+ var entityType = EntityModel.type;
+ $state.go("forms." + entityType + ".general", {'action':'import'}, {reload: true});
}
-
});
};
- $scope.goPage = function(page){
- $scope.loading = true;
+ $scope.goPage = function(page, type){
+ $scope.currentPage = page;
+ if (!(type && type == 'list')) {
+ $scope.loading = true;
+ }
+
var offset = (page-1) * resultsPerPage;
- EntityFalcon.searchEntities($scope.entityName, $scope.entityTags, $scope.entityType, offset).then(function() {
+ return EntityFalcon.searchEntities($scope.entityName, $scope.entityTags, $scope.entityType, offset).then(function() {
if (EntityFalcon.data !== null) {
$scope.actualPage = page;
$scope.searchList = EntityFalcon.data.entity;
@@ -72,14 +72,16 @@
$scope.pages[i].enabled = true;
}
}
- if($scope.searchList.length === 0){
+ if($scope.searchList.length === 0 && !(type && type == 'list')){
Falcon.warningMessage("No results matched the search criteria.");
}
$timeout(function() {
angular.element('#tagsInput').focus();
}, 0, false);
Falcon.responses.listLoaded = true;
- $scope.loading = false;
+ if (!(type && type == 'list')) {
+ $scope.loading = false;
+ }
}
});
};
@@ -128,13 +130,134 @@
};
$scope.cancel = function (type, state) {
+ var message = type + ' edition cancelled';
+ if(type === 'cluster'){
+ message = 'Create New Cluster operation cancelled'
+ }
var cancelInfo = {
state: state || $state.current.name,
- message: type + ' edition canceled '
+ message: message
};
Falcon.logResponse('cancel', cancelInfo, type, false);
};
+ $scope.displayResults = function (remove) {
+ $state.go("main");
+ $scope.refreshList($scope.tags);
+ (!remove) ? $scope.persistSearch($scope.tags) : '';
+
+ };
+
+ $scope.persistSearch = function(newTag){
+ var storedTags = $localStorage['SearchedTag'],
+ flagExit = false,
+ newval = newTag[newTag.length -1].text;
+
+
+ if(storedTags !== undefined && newTag !== undefined && newTag.length > 0){
+
+ for(var t=0; t<storedTags.length; t++){
+ if(storedTags[t].toLowerCase() === newval.toLowerCase()) {
+ flagExit = true;
+ break;
+ }
+ }
+ if(!flagExit ) {
+ storedTags.push(newval);
+ $localStorage['SearchedTag'] = storedTags;
+ }
+
+ if(storedTags.length > 5) { storedTags.splice(0,1); $localStorage['SearchedTag'] = storedTags; }
+
+ } else if(newTag !== undefined && newTag.length > 0) {
+ $localStorage['SearchedTag'] = [newval];
+ }
+
+ };
+
+ $scope.clearTags = function(){
+ $scope.tags = [];
+ $scope.refreshList($scope.tags);
+ };
+
+$scope.loadTags = function(query) {
+ var tags = new Array(), storedTags = $localStorage['SearchedTag'], tagAdded = false, tempTags = [];
+ if(!$scope.$parent.nameFounded){
+ tags.push({ text: 'Name:' + query });
+ tempTags.push('Name:' + query);
+ }
+
+ if(storedTags != undefined && storedTags.length > 0){
+ for(var e=0; e < storedTags.length; e++){
+ if(storedTags[e].toLowerCase().indexOf(query.toLowerCase()) != -1){
+ if(tempTags.indexOf(storedTags[e]) == -1) {
+ tags.push({ text: storedTags[e] });
+ tempTags.push('Name:' + query);
+ tagAdded = true;
+ }
+ }
+ }
+ }
+
+
+ if(!$scope.$parent.typeFounded && !tagAdded){
+ var queryAux = query.toUpperCase();
+ if(queryAux === "F" || queryAux === "FE" || queryAux === "FEE" || queryAux === "FEED"){
+ tags.push({ text: 'Type:feed'});
+ }
+ if(queryAux === "P" || queryAux === "PR" || queryAux === "PRO" || queryAux === "PROC" || queryAux === "PROCE"
+ || queryAux === "PROCES" || queryAux === "PROCESS"){
+ tags.push({ text: 'Type:process'});
+ }
+ if(queryAux === "M" || queryAux === "MI" || queryAux === "MIR" || queryAux === "MIRR" || queryAux === "MIRRO"
+ || queryAux === "MIRROR"){
+ tags.push({ text: 'Type:mirror'});
+ }
+ }
+ if(query !== "*"){
+ tags.push({ text: 'Tag:' + query });
+ }
+ return tags;
+ };
+
+ $scope.clusterInterfaceLabels = function(interfaceType) {
+ switch (interfaceType) {
+ case "readonly":
+ return "File System Read Endpoint Address";
+ case "write":
+ return "File System Default Address";
+ case "execute":
+ return "Yarn Resource Manager Address";
+ case "workflow":
+ return "Workflow Address";
+ case "messaging":
+ return "Message Broker Address";
+ case "registry":
+ return "Metadata Catalog Registry";
+ case "spark":
+ return "Spark";
+ default:
+ return "";
+ }
+ };
+
+ $scope.displayEntities = function (type) {
+ $state.go("main", { 'fromAction' : 'listEntities'});
+ $scope.entityType = type;
+ $scope.entityName = '';
+ $scope.entityTags = '';
+ $scope.goPage(1, 'list');
+ };
+
+ $scope.feedPropertiesLabels = {
+ queueName: 'Queue Name',
+ jobPriority: 'Job Priority',
+ timeout: 'Timeout',
+ parallel: 'Parallel',
+ maxMaps: 'Max Maps',
+ mapBandwidthKB: 'Map Bandwidth KB'
+ };
+
}]);
}());
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/falcon/blob/76dc2e18/falcon-ui/app/js/controllers/snapshot/snapshot-controller.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/snapshot/snapshot-controller.js b/falcon-ui/app/js/controllers/snapshot/snapshot-controller.js
new file mode 100644
index 0000000..3f047ce
--- /dev/null
+++ b/falcon-ui/app/js/controllers/snapshot/snapshot-controller.js
@@ -0,0 +1,262 @@
+/**
+ * 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.
+ */
+(function () {
+ 'use strict';
+
+ var snapshotModule = angular.module('app.controllers.snapshot', [ 'app.services' ]);
+
+ snapshotModule.controller('SnapshotController', [
+ "$scope", "$interval", "$controller", "Falcon", "EntityModel", "$state", "X2jsService", "DateHelper",
+ "RouteHelper", "ValidationService", "SpinnersFlag", "$timeout", "$rootScope", "clustersList",
+ "$cookieStore", "SnapshotModel", "EntityFactory", "ExtensionSerializer",
+ function ($scope, $interval, $controller, Falcon, EntityModel, $state, X2jsService, DateHelper,
+ RouteHelper, validationService, SpinnersFlag, $timeout, $rootScope, clustersList,
+ $cookieStore, snapshotModel, entityFactory, extensionSerializer) {
+
+ var stateMatrix = {
+ general : {previous : '', next : 'summary'},
+ summary : {previous : 'general', next : ''}
+ };
+
+ $scope.entityType = 'snapshot';
+ $scope.skipUndo = false;
+ $scope.clusterErrorMessage = '';
+ unwrapClusters(clustersList);
+
+ //extending root controller
+ $controller('EntityRootCtrl', {
+ $scope: $scope
+ });
+
+ $scope.$on('$destroy', function() {
+ var defaultProcess = entityFactory.newEntity('snapshot'),
+ nameIsEqual = ($scope.snapshot.name == null || $scope.snapshot.name === ""),
+ ACLIsEqual = angular.equals($scope.snapshot.ACL, defaultProcess.ACL);
+
+ if (!$scope.skipUndo && (!nameIsEqual || !ACLIsEqual)) {
+ $scope.$parent.models.snapshotModel = $scope.snapshot;
+ if ($scope.cloningMode) {
+ $scope.$parent.models.snapshotModel.clone = true;
+ }
+ if ($scope.editingMode) {
+ $scope.$parent.models.snapshotModel.edit = true;
+ }
+ $scope.$parent.cancel('snapshot', $rootScope.previousState);
+ }
+ });
+
+ $scope.isActive = function (route) {
+ return route === $state.current.name;
+ };
+
+ $scope.isCompleted = function (route) {
+ return $state.get(route).data && $state.get(route).data.completed;
+ };
+
+ $scope.loadOrCreateEntity = function() {
+ var type = $scope.entityType;
+
+ if(!snapshotModel && $scope.$parent.models.snapshotModel) {
+ var snapshotObj = $scope.$parent.models.snapshotModel;
+ $scope.$parent.models.snapshotModel = null;
+ return snapshotObj;
+ }
+ $scope.$parent.models.snapshotModel = null;
+ return snapshotModel ? extensionSerializer.serializeExtensionModel(snapshotModel, 'snapshot')
+ : entityFactory.newEntity(type);
+ };
+
+ $scope.init = function() {
+ $scope.baseInit();
+ var type = $scope.entityType;
+ $scope[type] = $scope.loadOrCreateEntity();
+ if(snapshotModel && snapshotModel.clone === true) {
+ $scope.cloningMode = true;
+ $scope.editingMode = false;
+ $scope[type].name = "";
+ } else if(snapshotModel && snapshotModel.edit === true) {
+ $scope.editingMode = true;
+ $scope.cloningMode = false;
+ } else{
+ $scope.editingMode = false;
+ $scope.cloningMode = false;
+ }
+ console.log($scope.editingMode);
+ }
+
+ $scope.init();
+
+ //----------------TAGS---------------------//
+ $scope.addTag = function () {
+ $scope.snapshot.tags.push({key: null, value: null});
+ };
+
+ $scope.removeTag = function (index) {
+ if (index >= 0 && $scope.snapshot.tags.length > 1) {
+ $scope.snapshot.tags.splice(index, 1);
+ }
+ };
+
+ //----------- Alerts -----------//
+ $scope.addAlert = function () {
+ $scope.snapshot.alerts.push($scope.snapshot.alert.email);
+ $scope.snapshot.alert = {email: ""};
+ };
+ $scope.removeAlert = function (index) {
+ $scope.snapshot.alerts.splice(index, 1);
+ };
+
+ //----------------- DATE INPUTS -------------------//
+ $scope.dateFormat = 'MM/dd/yyyy';
+
+ $scope.openStartDatePicker = function ($event) {
+ $event.preventDefault();
+ $event.stopPropagation();
+ $scope.startOpened = true;
+ };
+
+ $scope.openEndDatePicker = function ($event) {
+ $event.preventDefault();
+ $event.stopPropagation();
+ $scope.endOpened = true;
+ };
+
+ $scope.constructDate = function () {
+ if ($scope.snapshot.validity.start && $scope.snapshot.validity.end
+ && $scope.snapshot.validity.startTime && $scope.snapshot.validity.endTime) {
+ $scope.snapshot.validity.startISO = DateHelper.createISO(
+ $scope.snapshot.validity.start, $scope.snapshot.validity.startTime, $scope.snapshot.validity.timezone);
+ $scope.snapshot.validity.endISO = DateHelper.createISO(
+ $scope.snapshot.validity.end, $scope.snapshot.validity.endTime, $scope.snapshot.validity.timezone);
+ }
+ };
+
+ $scope.$watch(function () {
+ return $scope.snapshot.validity.timezone;
+ }, function () {
+ return $scope.constructDate();
+ });
+
+ $scope.validateCluster = function() {
+ if ($scope.snapshot.source.cluster === $scope.snapshot.target.cluster) {
+ $scope.clusterErrorMessage = 'Target cannot be the same as the Source';
+ } else {
+ $scope.clusterErrorMessage = '';
+ }
+ };
+
+ $scope.goNext = function (formInvalid) {
+ $state.current.data = $state.current.data || {};
+ $state.current.data.completed = !formInvalid;
+
+ SpinnersFlag.show = true;
+ if (!validationService.nameAvailable || formInvalid) {
+ validationService.displayValidations.show = true;
+ validationService.displayValidations.nameShow = true;
+ SpinnersFlag.show = false;
+ return;
+ }
+ if ($scope.clusterErrorMessage !== '') {
+ SpinnersFlag.show = false;
+ return;
+ }
+ validationService.displayValidations.show = false;
+ validationService.displayValidations.nameShow = false;
+ $state.go(RouteHelper.getNextState($state.current.name, stateMatrix));
+ angular.element('body, html').animate({scrollTop: 0}, 500);
+ };
+
+ $scope.goBack = function () {
+ SpinnersFlag.backShow = true;
+ validationService.displayValidations.show = false;
+ validationService.displayValidations.nameShow = false;
+ $state.go(RouteHelper.getPreviousState($state.current.name, stateMatrix));
+ angular.element('body, html').animate({scrollTop: 0}, 500);
+ };
+
+ $scope.save = function (formInvalid) {
+ SpinnersFlag.saveShow = true;
+
+ $state.current.data = $state.current.data || {};
+ $state.current.data.completed = !formInvalid;
+ if (!validationService.nameAvailable || formInvalid) {
+ validationService.displayValidations.show = true;
+ validationService.displayValidations.nameShow = true;
+ SpinnersFlag.saveShow = false;
+ return;
+ }
+ if ($scope.clusterErrorMessage !== '') {
+ SpinnersFlag.saveShow = false;
+ return;
+ }
+ validationService.displayValidations.show = false;
+ validationService.displayValidations.nameShow = false;
+
+ var snapshotData = extensionSerializer.convertObjectToString(
+ extensionSerializer.serializeExtensionProperties($scope.snapshot, 'snapshot'));
+
+ if($scope.editingMode) {
+ Falcon.postUpdateExtension(snapshotData, 'HDFS-SNAPSHOT-MIRRORING')
+ .success(function (response) {
+ $scope.skipUndo = true;
+ Falcon.logResponse('success', response, false);
+ SpinnersFlag.saveShow = false;
+ $state.go('main');
+
+ })
+ .error(function (err) {
+ Falcon.logResponse('error', err, false);
+ SpinnersFlag.saveShow = false;
+ angular.element('body, html').animate({scrollTop: 0}, 300);
+ });
+ } else {
+ Falcon.postSubmitExtension(snapshotData, 'HDFS-SNAPSHOT-MIRRORING')
+ .success(function (response) {
+ $scope.skipUndo = true;
+ Falcon.logResponse('success', response, false);
+ SpinnersFlag.saveShow = false;
+ $state.go('main');
+ })
+ .error(function (err) {
+ Falcon.logResponse('error', err, false);
+ SpinnersFlag.saveShow = false;
+ angular.element('body, html').animate({scrollTop: 0}, 300);
+ });
+ }
+ };
+
+ function unwrapClusters(clusters) {
+ if(clusters !== undefined && clusters !== null && clusters !== "null"){
+ $scope.clusterList = [];
+ var typeOfData = Object.prototype.toString.call(clusters.entity);
+ if(typeOfData === "[object Array]") {
+ $scope.clusterList = clusters.entity;
+ } else if(typeOfData === "[object Object]") {
+ $scope.clusterList = [clusters.entity];
+ } else {
+ console.log("type of data not recognized");
+ }
+ }
+ }
+
+ if($state.current.name !== "forms.snapshot.general"){
+ $state.go("forms.snapshot.general");
+ }
+
+ }]);
+}());
http://git-wip-us.apache.org/repos/asf/falcon/blob/76dc2e18/falcon-ui/app/js/controllers/themeController.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/controllers/themeController.js b/falcon-ui/app/js/controllers/themeController.js
new file mode 100644
index 0000000..513b19e
--- /dev/null
+++ b/falcon-ui/app/js/controllers/themeController.js
@@ -0,0 +1,31 @@
+/**
+ * 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.
+ */
+(function() {
+ 'use strict';
+
+ var app = angular.module('app.controllers.theme', []);
+
+ app.controller('ThemeController', ["$scope", function($scope) {
+
+ $scope.theme = 'default';
+ $scope.themeChange = function(theme) {
+ $scope.theme = theme;
+ };
+ }]);
+
+}());
http://git-wip-us.apache.org/repos/asf/falcon/blob/76dc2e18/falcon-ui/app/js/directives/acl-permissions.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/directives/acl-permissions.js b/falcon-ui/app/js/directives/acl-permissions.js
new file mode 100644
index 0000000..98ef508
--- /dev/null
+++ b/falcon-ui/app/js/directives/acl-permissions.js
@@ -0,0 +1,103 @@
+/**
+ * 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.
+ */
+(function () {
+ 'use strict';
+
+ var aclPermissionsModule = angular.module('app.directives.acl-permissions',[]);
+
+ aclPermissionsModule.directive('aclPermissions', ['$timeout',function ($timeout) {
+ return {
+ restrict : 'EA',
+ templateUrl: 'html/directives/aclPermissions.html',
+ link: function ($scope, element, attrs) {
+
+ var permission = {
+ 'owner' : 0,
+ 'groups' : 0,
+ 'others' : 0
+ }
+
+ function getNestedProperty(obj, path) {
+ var paths = path.split('.'),
+ current = obj;
+ for (var i = 0; i < paths.length; i++) {
+ if (current[paths[i]] == undefined) {
+ return undefined;
+ } else {
+ current = current[paths[i]];
+ }
+ }
+ return current;
+ };
+
+ function setNestedProperty(obj, path, value) {
+ var paths = path.split('.'),
+ current = obj;
+ for (var i = 0; i < paths.length -1; i++) {
+ current = current[paths[i]];
+ }
+ current[paths[paths.length - 1]] = value;
+ };
+
+ var defaultPermission = getNestedProperty($scope, attrs.aclModel);
+
+ function setPermission() {
+ if(defaultPermission){
+ var permissionArray = defaultPermission.substring(2).split("");
+ permissionArray.forEach(function(value, index){
+ var permissionValue = parseInt(value);
+ var type = Object.keys(permission)[index];
+ permission[type] = value;
+ var checked = element.find('input[name=' + type + ']');
+ var readPermission = 4,
+ writePermission = 2,
+ executePermission = 1;
+ if(permissionValue & readPermission){
+ angular.element(checked[0]).attr('checked','checked');
+ }
+ if(permissionValue & writePermission){
+ angular.element(checked[1]).attr('checked','checked');
+ }
+ if(permissionValue & executePermission){
+ angular.element(checked[2]).attr('checked','checked');
+ }
+ });
+ }
+ };
+
+ $scope.calculatePermission = function(type){
+ var checked = element.find('input[name=' + type + ']:checked');
+ var total = 0;
+ angular.forEach(checked,function(element){
+ total += parseInt(element.value);
+ });
+ permission[type] = total;
+ var effectivePermission = '0x';
+ angular.forEach(permission, function(value,key){
+ effectivePermission = effectivePermission + value;
+ });
+ setNestedProperty($scope, attrs.aclModel, effectivePermission);
+ };
+
+ $timeout(function () {
+ setPermission();
+ }, 0);
+ }
+ };
+ }]);
+})();
http://git-wip-us.apache.org/repos/asf/falcon/blob/76dc2e18/falcon-ui/app/js/directives/chart.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/directives/chart.js b/falcon-ui/app/js/directives/chart.js
index f9d6983..f630a9f 100644
--- a/falcon-ui/app/js/directives/chart.js
+++ b/falcon-ui/app/js/directives/chart.js
@@ -453,7 +453,7 @@
});
- d3Module.controller('chartCtrl', [ "$scope", "Falcon", function($scope, Falcon) {
+ d3Module.controller('chartCtrl', [ "$scope", "Falcon", "DateHelper", function($scope, Falcon, DateHelper) {
var formatFL = d3.time.format.utc("%A %d"),
formatSL = d3.time.format.utc("%b %Y"),
@@ -500,7 +500,7 @@
};
- $scope.dateFormat ='MM/dd/yyyy';
+ $scope.dateFormat = DateHelper.getLocaleDateFormat();
$scope.openDatePicker = function($event) {
$event.preventDefault();
$event.stopPropagation();
http://git-wip-us.apache.org/repos/asf/falcon/blob/76dc2e18/falcon-ui/app/js/directives/check-name.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/directives/check-name.js b/falcon-ui/app/js/directives/check-name.js
index bea0e22..edb3697 100644
--- a/falcon-ui/app/js/directives/check-name.js
+++ b/falcon-ui/app/js/directives/check-name.js
@@ -20,24 +20,28 @@
var checkNameModule = angular.module('app.directives.check-name', ['app.services.falcon', 'app.services.validation']);
- checkNameModule.directive('checkName', [ "ValidationService", "$timeout", "Falcon", "EntityFalcon", function (validationService, $timeout, Falcon, EntityFalcon) {
+ checkNameModule.directive('checkName', [ "ValidationService", "$timeout", "Falcon", "EntityFalcon","$q", function (validationService, $timeout, Falcon, EntityFalcon,$q) {
return {
- replace: false,
scope: {
checkName: "="
},
- restrict: 'A',
- link: function (scope, element) {
-
- var options = scope.checkName,
- //entities = scope.$parent.lists[options.type + 'List'],
- type = options.type,
- name = element[0].value;
-
+ require: ['ngModel','^form'],
+ link: function(scope, element, attrs, ctrls) {
+ var options = scope.checkName,
+ //entities = scope.$parent.lists[options.type + 'List'],
+ type = options.type,
+ name = element[0].value,
+ errorMsg,
+ ngModelCtrl = ctrls[0],
+ formCtrl = ctrls[1];
if (!options.check) {
return;
}
+ element.parent()
+ .append("<div class='nameInputDisplay hidden'>" +
+ "</div><label class='custom-danger nameValidationMessage'></label>");
+
scope.$watch(function () {
return element[0].value;
}, function () {
@@ -45,121 +49,81 @@
if (element[0].value.length === 0) {
element.addClass('empty');
}
- getNameAvailability(function() {
- getMessage();
- if (element.hasClass('ng-valid') && validationService.nameAvailable) {
- angular.element('.nameValidationMessage').addClass('hidden');
- } else {
- element.parent().addClass("showValidationStyle");
- angular.element('.nameValidationMessage').removeClass('hidden');
- element.removeClass('empty');
- }
- });
}
-
-
});
- function getLabels() {
- element.parent()
- .append("<div class='nameInputDisplay hidden'>" +
- "</div><label class='custom-danger nameValidationMessage'></label>");
- }
-
- function getNameAvailability(fn) {
- name = element[0].value;
- if (name.length === 0) {
- angular.element('.nameInputDisplay').addClass('hidden');
- }else{
- Falcon.logRequest();
- Falcon.getEntityDefinition(type, name).success(function (data) {
- Falcon.logResponse('success', data, false, true);
- validationService.nameAvailable = false;
- if (name.length === 0) {
- angular.element('.nameInputDisplay').addClass('hidden');
- } else if (!validationService.nameAvailable && name.length > 0 && element.hasClass('ng-valid')) {
- angular.element('.nameInputDisplay').html('Name unavailable')
- .removeClass('custom-success hidden').addClass('custom-danger');
- } else if (validationService.nameAvailable && name.length > 0 && element.hasClass('ng-valid')) {
- angular.element('.nameInputDisplay').html('Name available')
- .removeClass('custom-danger hidden').addClass('custom-success');
- } else if (element.hasClass('ng-invalid-pattern') && name.length > 0) {
- angular.element('.nameInputDisplay').addClass('hidden');
- }
- if (fn) { fn(); } //>callback
- }).error(function (err) {
- Falcon.logResponse('error', err, false, true);
- validationService.nameAvailable = true;
- if (name.length === 0) {
- angular.element('.nameInputDisplay').addClass('hidden');
- } else if (!validationService.nameAvailable && name.length > 0 && element.hasClass('ng-valid')) {
- angular.element('.nameInputDisplay').html('Name unavailable')
- .removeClass('custom-success hidden').addClass('custom-danger');
- } else if (validationService.nameAvailable && name.length > 0 && element.hasClass('ng-valid')) {
- angular.element('.nameInputDisplay').html('Name available')
- .removeClass('custom-danger hidden').addClass('custom-success');
- } else if (element.hasClass('ng-invalid-pattern') && name.length > 0) {
- angular.element('.nameInputDisplay').addClass('hidden');
+ scope.$watch(function(){
+ return ngModelCtrl.$valid;
+ }, function(newValue, oldValue){
+ if(ngModelCtrl.$dirty && newValue){
+ errorMsg = "Name available";
+ validationService.nameAvailable = true;
+ }else {
+ if(ngModelCtrl.$error.uniqueName){
+ errorMsg = validationService.messages.name.unavailable;
+ validationService.nameAvailable = false;
+ }else if(ngModelCtrl.$error.required){
+ errorMsg = validationService.messages.name.empty;
+ }else if(ngModelCtrl.$error.pattern){
+ errorMsg = validationService.messages.name.patternInvalid;
}
- if (fn) { fn(); } //>callback
- });
}
+ });
- }
-
- function getMessage() {
- if (name.length === 0) {
- element.addClass('empty');
- angular.element('.nameValidationMessage').html(validationService.messages.name.empty).addClass('hidden');
-
- } else if (!validationService.nameAvailable && name.length > 0 && element.hasClass('ng-valid')) {
- element.addClass('empty');
- angular.element('.nameValidationMessage')
- .html(validationService.messages.name.unavailable).addClass('hidden');
-
- } else if (element.hasClass('ng-invalid-pattern') && name.length > 0) {
- element.removeClass('empty');
+ scope.$watch(function(){
+ return errorMsg;
+ },function(newValue, oldValue){
+ if(newValue && newValue.length > 0){
element.parent().addClass("showValidationStyle");
- angular.element('.nameValidationMessage')
- .html(validationService.messages.name.patternInvalid).removeClass('hidden');
-
- } else if (element.hasClass('ng-valid') && name.length > 0) {
+ element.removeClass('empty');
+ if(errorMsg ==='Name available'){
+ angular.element('.nameInputDisplay').removeClass('custom-danger hidden').addClass('custom-success');
+ angular.element('.nameInputDisplay').text(newValue);
+ angular.element('.nameValidationMessage').addClass("hidden");
+ }else{
+ angular.element('.nameValidationMessage').text(newValue);
+ angular.element('.nameValidationMessage').removeClass('hidden').addClass('custom custom-danger');
+ angular.element('.nameInputDisplay').addClass("hidden");
+ }
+ }else{
element.parent().removeClass("showValidationStyle");
- angular.element('.nameValidationMessage').addClass('hidden');
}
- }
- function addListeners() {
- element.bind('keyup', function () {
- getNameAvailability();
- getMessage();
- });
- element.bind('focus', function () {
- element.removeClass('empty');
- });
- element.bind('blur', function () {
- if (element.hasClass('ng-valid') && validationService.nameAvailable) {
- angular.element('.nameValidationMessage').addClass('hidden');
+ });
- } else {
- element.parent().addClass("showValidationStyle");
- angular.element('.nameValidationMessage').removeClass('hidden');
- element.removeClass('empty');
- }
+ ngModelCtrl.$asyncValidators.uniqueName = function isNameAvailable(modelValue,viewValue) {
+ Falcon.logRequest();
+ var def = $q.defer();
+ Falcon.getEntityDefinition(type, modelValue).success(function (data) {
+ Falcon.logResponse('success', data, false, true);
+ def.reject();
+ }).error(function (err) {
+ Falcon.logResponse('error', err, false, true);
+ def.resolve();
});
+ return def.promise;
}
- function init() {
- getLabels();
- addListeners();
- getNameAvailability();
- getMessage();
+ element.bind('blur',function(event){
+ if(ngModelCtrl.$valid){
+ angular.element('.nameValidationMessage').addClass('hidden');
+ }else{
+ angular.element('.nameValidationMessage').removeClass('hidden');
+ }
+ });
- $timeout(function () { element.trigger('focus'); }, 20);
- }
+ element.bind('focus',function(event){
+ if(formCtrl.$submitted && ngModelCtrl.$pristine && ngModelCtrl.$error.required){
+ angular.element('.nameValidationMessage').removeClass('hidden');
+ element.parent().addClass('showValidationStyle');
+ }else{
+ angular.element('.nameValidationMessage').addClass('hidden');
+ element.parent().removeClass('showValidationStyle');
+ }
+ });
- init();
+ $timeout(function () { element.trigger('focus'); }, 0);
}
- };
+ }
}]);
-}());
\ No newline at end of file
+}());
http://git-wip-us.apache.org/repos/asf/falcon/blob/76dc2e18/falcon-ui/app/js/directives/dependencies-graph.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/directives/dependencies-graph.js b/falcon-ui/app/js/directives/dependencies-graph.js
index c8faa74..bd37358 100644
--- a/falcon-ui/app/js/directives/dependencies-graph.js
+++ b/falcon-ui/app/js/directives/dependencies-graph.js
@@ -18,17 +18,16 @@
(function () {
'use strict';
- var entitiesListModule = angular.module('app.directives.dependencies-graph', ['app.services' ]);
+ var entitiesListModule = angular.module('app.directives.dependencies-graph', ['app.services' ]);
- entitiesListModule.controller('DependenciesGraphCtrl', ['$scope', 'Falcon', 'X2jsService', '$window', 'EncodeService', 'EntityModel',
- function($scope, Falcon, X2jsService, $window, encodeService, EntityModel) {
+ entitiesListModule.controller('DependenciesGraphCtrl', ['$scope', 'Falcon', 'X2jsService', '$window', 'EncodeService',
+ function($scope, Falcon, X2jsService, $window, encodeService) {
- }]);
+ }]);
- entitiesListModule.directive('dependenciesGraph', ["$timeout", 'Falcon', '$filter', '$state', 'X2jsService', 'EntityModel',
- function($timeout, Falcon, $filter, $state, X2jsService, EntityModel) {
+ entitiesListModule.directive('dependenciesGraph', ["$timeout", 'Falcon', '$filter', function($timeout, Falcon, $filter) {
return {
scope: {
type: "=",
@@ -49,16 +48,17 @@
return type + '/' + name;
}
- function getOrCreateNode(type, name) {
+ function getOrCreateNode(type, name, tag) {
var k = key(type, name);
if (nodes[k] !== undefined)
return nodes[k];
var n = {
- "id": next_node_id++,
+ "guid": next_node_id++,
"type": type,
"name": name,
- "dependency": []
+ "arrowDirections": tag,
+ "children": []
};
nodes[k] = n;
return n;
@@ -81,7 +81,8 @@
var l = data.entity.length;
for (var i = 0; i < l; ++i) {
var e = data.entity[i];
- var d = getOrCreateNode(e.type, e.name);
+ var tag = e.hasOwnProperty('tags') && e.tags !== null ? e.tags.tag : "Input";
+ var d = getOrCreateNode(e.type, e.name, tag);
var src = null, dst = null;
if (d.type === "cluster") {
src = node; dst = d;
@@ -94,11 +95,10 @@
src = node; dst = d;
}
}
- //console.log(src.name + '->' + dst.name);
- src.dependency.push(dst.id);
+ src.children.push(d);
}
- done_callback(nodes);
+ done_callback(node);
})
.error(function (err) {
Falcon.logResponse('error', err, false, true);
@@ -106,181 +106,141 @@
}
function load() {
- var n = getOrCreateNode(entity_type, entity_name);
+ var n = getOrCreateNode(entity_type, entity_name, "Input");
loadEntry(n);
}
load();
};
- var plotDependencyGraph = function(nodes, element) {
- var NODE_WIDTH = 150;
- var NODE_HEIGHT = 50;
- var RECT_ROUND = 10;
- var SEPARATION = 40;
- var UNIVERSAL_SEP = 80;
-
- var svg = d3.select(element).append("svg");
-
+ var plotDependencyGraph = function(nodes, container) {
+ var element = d3.select(container.element),
+ width = Math.max(container.width, 960),
+ height = Math.max(container.height, 300);
+
+ var margin = {
+ top: 30,
+ right: 30,
+ bottom: 30,
+ left: 80
+ };
+ width = width - margin.right - margin.left;
+ height = height - margin.top - margin.bottom;
// Function to draw the lines of the edge
- var LINE_FUNCTION = d3.svg.line()
- .x(function(d) { return d.x; })
- .y(function(d) { return d.y; })
- .interpolate('basis');
-
- // Mappining from id to a node
- var node_by_id = {};
-
- var layout = null;
-
- /**
- * Calculate the intersection point between the point p and the edges of the rectangle rect
- **/
- function intersectRect(rect, p) {
- var cx = rect.x, cy = rect.y, dx = p.x - cx, dy = p.y - cy, w = rect.width / 2, h = rect.height / 2;
-
- if (dx == 0)
- return { "x": p.x, "y": rect.y + (dy > 0 ? h : -h) };
-
- var slope = dy / dx;
-
- var x0 = null, y0 = null;
- if (Math.abs(slope) < rect.height / rect.width) {
- // intersect with the left or right edges of the rect
- x0 = rect.x + (dx > 0 ? w : -w);
- y0 = cy + slope * (x0 - cx);
- } else {
- y0 = rect.y + (dy > 0 ? h : -h);
- x0 = cx + (y0 - cy) / slope;
- }
-
- return { "x": x0, "y": y0 };
- }
-
- function drawNode(u, value) {
- var root = svg.append('g').classed('node', true)
- .attr('transform', 'translate(' + -value.width/2 + ',' + -value.height/2 + ')');
-
- var node = node_by_id[u];
-
-
+ var i = 0;
- var fo = root.append('foreignObject')
- .attr('x', value.x)
- .attr('y', value.y)
- .attr('width', value.width)
- .attr('height', value.height)
- .attr('class', 'foreignObject');
-
-
- var txt = fo.append('xhtml:div')
- .text(node.name)
- .classed('node-name', true)
- .classed('node-name-' + node.type, true);
-
- var rect = root.append('rect')
- .attr('width', value.width)
- .attr('height', value.height)
- .attr('x', value.x)
- .attr('y', value.y)
- .attr('rx', RECT_ROUND)
- .attr('ry', RECT_ROUND)
-
- .on('click', function () {
-
- Falcon.logRequest();
- Falcon.getEntityDefinition(node.type.toLowerCase(), node.name)
- .success(function (data) {
- Falcon.logResponse('success', data, false, true);
- var entityModel = X2jsService.xml_str2json(data);
- EntityModel.type = node.type.toLowerCase();
- EntityModel.name = node.name;
- EntityModel.model = entityModel;
- $state.go('entityDetails');
- })
- .error(function (err) {
- Falcon.logResponse('error', err, false, false);
- });
-
+ var tree = d3.layout.tree()
+ .size([height, width]);
+ var diagonal = d3.svg.diagonal()
+ .projection(function(d) {
+ return [d.y, d.x];
});
- }
-
- function drawEdge(e, u, v, value) {
- var root = svg.append('g').classed('edge', true);
-
- root.append('path')
- .attr('marker-end', 'url(#arrowhead)')
- .attr('d', function() {
- var points = value.points;
-
- var source = layout.node(u);
- var target = layout.node(v);
-
- var p0 = points.length === 0 ? target : points[0];
- var p1 = points.length === 0 ? source : points[points.length - 1];
-
- points.unshift(intersectRect(source, p0));
- points.push(intersectRect(target, p1));
-
- return LINE_FUNCTION(points);
+ var svg = element.select('svg')
+ .attr('width', width + margin.right + margin.left)
+ .attr('height', height + margin.top + margin.bottom)
+ .select('g')
+ .attr('transform',
+ 'translate(' + margin.left + ',' + margin.right + ')');
+
+ svg.append("svg:defs").append("svg:marker").attr("id", "output-arrow").attr("viewBox", "0 0 10 10")
+ .attr("refX", 20).attr("refY", 5).attr("markerUnits", "strokeWidth").attr("markerWidth", 6)
+ .attr("markerHeight", 9).attr("orient", "auto").append("svg:path").attr("d", "M 0 0 L 10 5 L 0 10 z");
+
+ //marker for input type graph
+ svg.append("svg:defs")
+ .append("svg:marker")
+ .attr("id", "input-arrow")
+ .attr("viewBox", "0 0 10 10")
+ .attr("refX", -7)
+ .attr("refY", 5)
+ .attr("markerUnits", "strokeWidth")
+ .attr("markerWidth", 6)
+ .attr("markerHeight", 9)
+ .attr("orient", "auto")
+ .append("svg:path")
+ .attr("d", "M -2 5 L 8 0 L 8 10 z");
+
+ var root = nodes;
+ function update(source) {
+
+ // Compute the new tree layout.
+ var nodes1 = tree.nodes(source).reverse(),
+ links = tree.links(nodes1);
+ // Normalize for fixed-depth.
+ nodes1.forEach(function(d) {
+ d.y = d.depth * 180;
+ });
+
+ // Declare the nodes\ufffd
+ var node = svg.selectAll('g.node')
+ .data(nodes1, function(d) {
+
+ return d.id || (d.id = ++i);
+ });
+ // Enter the nodes.
+ var nodeEnter = node.enter().append('g')
+ .attr('class', 'node')
+ .attr('transform', function(d) {
+ return 'translate(' + d.y + ',' + d.x + ')';
});
- }
- function postRender() {
- svg
- .append('svg:defs')
- .append('svg:marker')
- .attr('id', 'arrowhead')
- .attr('viewBox', '0 0 10 10')
- .attr('refX', 8)
- .attr('refY', 5)
- .attr('markerUnits', 'strokewidth')
- .attr('markerWidth', 8)
- .attr('markerHeight', 5)
- .attr('orient', 'auto')
- .attr('style', 'fill: #333')
- .append('svg:path')
- .attr('d', 'M 0 0 L 10 5 L 0 10 z');
- }
+ nodeEnter.append("image")
+ .attr("xlink:href", function(d) {
+ //return d.icon;
+ return d.type === 'cluster' ? 'css/img/cloud.png' : 'css/img/feed.png';
+ })
+ .attr("x", "-18px")
+ .attr("y", "-18px")
+ .attr("width", "34px")
+ .attr("height", "34px");
+
+ nodeEnter.append('text')
+ .attr('x', function(d) {
+ return d.children || d._children ?
+ (5) * -1 : +15;
+ })
+ .attr('dy', '-1.75em')
+ .attr('text-anchor', function(d) {
+ return d.children || d._children ? 'middle' : 'middle';
+ })
+ .text(function(d) {
+ return d.name;
+ })
- function plot() {
- var g = new dagre.Digraph();
+ .style('fill-opacity', 1);
- for (var key in nodes) {
- var n = nodes[key];
- node_by_id[n.id] = n;
- g.addNode(n.id, { "width": NODE_WIDTH, "height": NODE_HEIGHT });
- }
+ // Declare the links\ufffd
+ var link = svg.selectAll('path.link')
+ .data(links, function(d) {
+ return d.target.id;
+ });
- for (var key in nodes) {
- var n = nodes[key];
- for (var i = 0, l = n.dependency.length; i < l; ++i) {
- var d = n.dependency[i];
- g.addEdge(null, n.id, d);
- }
- }
+ link.enter().insert('path', 'g')
+ .attr('class', 'link')
+ //.style('stroke', function(d) { return d.target.level; })
+ .style('stroke', 'green')
+ .attr('d', diagonal);
+ link.attr("marker-start", function (d) {
+ if(d.target.arrowDirections==="Input")
+ return "url(#input-arrow)";
+ }); //if input
+ link.attr("marker-end", function (d) {
+ if(d.target.arrowDirections==="Output")
+ return "url(#output-arrow)";
+ }); //if outPut
- layout = dagre.layout()
- .universalSep(UNIVERSAL_SEP).rankSep(SEPARATION)
- .run(g);
- layout.eachEdge(drawEdge);
- layout.eachNode(drawNode);
+ }
- var graph = layout.graph();
+ update(root);
- svg.attr("width", graph.width);
- svg.attr("height", graph.height);
- postRender();
- }
- plot();
};
var visualizeDependencyGraph = function(type, name) {
loadDependencyGraph(type, name, function(nodes) {
- plotDependencyGraph(nodes, element[0]);
+ plotDependencyGraph(nodes, {element:element[0], height:element[0].offsetHeight,width:element[0].offsetWidth});
});
};
http://git-wip-us.apache.org/repos/asf/falcon/blob/76dc2e18/falcon-ui/app/js/directives/directives.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/directives/directives.js b/falcon-ui/app/js/directives/directives.js
index 9c388ea..e1366b6 100644
--- a/falcon-ui/app/js/directives/directives.js
+++ b/falcon-ui/app/js/directives/directives.js
@@ -29,9 +29,21 @@
'app.directives.validation-message',
'chart-module',
'app.directives.dependencies-graph',
- 'app.directives.lineage-graph'
+ 'app.directives.lineage-graph',
+ 'tooltip',
+ 'app.directives.feed-cluster-partitions',
+ 'app.directives.acl-permissions',
+ 'app.directives.interface-endpoint'
]);
+ directivesModule.directive('errorNav', function () {
+ return {
+ replace: false,
+ restrict: 'A',
+ templateUrl: 'html/error.html'
+ };
+ });
+
directivesModule.directive('navHeader', function () {
return {
replace: false,
@@ -74,7 +86,7 @@
template: '{{output}}',
link: function (scope) {
if (scope.value.quantity) {
- scope.output = scope.prefix + ' ' + scope.value.quantity + ' ' + scope.value.unit;
+ scope.output = (scope.prefix ? scope.prefix + ' ' : '') + scope.value.quantity + ' ' + scope.value.unit;
} else {
scope.output = 'Not specified';
}
@@ -87,16 +99,21 @@
restrict: 'E',
replace: false,
scope: {
- ngModel: '='
+ ngModel: '=',
+ required: '='
},
templateUrl: 'html/directives/timeZoneSelectDv.html'
};
});
- directivesModule.directive('simpleDate', ['$filter', function ($filter) {
+ directivesModule.directive('simpleDate', ['$filter','DateHelper', function ($filter, DateHelper) {
return {
require: 'ngModel',
link: function (scope, element, attrs, ngModelController) {
+ var dateFormat = DateHelper.getLocaleDateFormat();
+
+ element.attr('title','Date should be entered in '+ dateFormat.toLowerCase() + ' format.');
+
ngModelController.$parsers.push(function (data) {
//convert data from view format to model format
return data;
@@ -104,7 +121,7 @@
ngModelController.$formatters.push(function (date) {
//convert data from model format to view format
if (date !== "") {
- date = $filter('date')(date, 'MM/dd/yyyy');
+ date = $filter('date')(date, dateFormat);
}
return date;
});
@@ -135,6 +152,7 @@
resize();
});
var resize = function () {
+ element[0].style.resize = "vertical";
element[0].style.height = "250px";
return element[0].style.height = "" + element[0].scrollHeight + "px";
};
@@ -179,4 +197,259 @@
};
}]);
-}());
\ No newline at end of file
+ directivesModule.directive('scrollToError', ['$timeout',function ($timeout) {
+ return {
+ require : "^form",
+ restrict : 'A',
+ link: function (scope, element,attrs,form) {
+ element.on('mousedown',function(event){
+ event.preventDefault();
+ });
+ element.on('click', function () {
+ var formElement = angular.element('form[name="' + form.$name + '"]');
+ var firstInvalid = formElement[0].querySelector('.ng-invalid');
+ $timeout(function() {
+ if (firstInvalid) {
+ firstInvalid.blur();
+ firstInvalid.focus();
+ }
+ },0)
+ });
+ }
+ };
+ }]);
+
+ directivesModule.directive('feedFormClusterDetails', function () {
+ return {
+ replace: false,
+ restrict: 'EA',
+ templateUrl: 'html/feed/feedFormClusterDetailsTpl.html',
+ link: function ($scope, $element) {
+ $scope.$on('forms.feed.clusters:submit', function() {
+ $scope.cluster.isAccordionOpened = $element.find('.ng-invalid').length > 0;
+ });
+ }
+ };
+ });
+
+ directivesModule.directive('feedFormDataSource', function () {
+ return {
+ replace: false,
+ restrict: 'EA',
+ templateUrl: 'html/feed/feedFormDataSourceTpl.html',
+ link: function ($scope, $element) {
+ if($scope.feed.dataTransferType === 'import'){
+ $scope.dataSourceType = 'source';
+ $scope.dataTransferAction = 'extract';
+ if ($scope.feed.import === undefined) {
+ $scope.feed.import = { 'source' : {
+ 'extract' : {'type' : 'full', 'mergepolicy' : 'snapshot'}, 'columnsType' : 'all', 'fields' : {} } };
+ } else {
+ if ($scope.feed.import.source.fields && $scope.feed.import.source.fields.exlcudes) {
+ $scope.feed.import.source.columnsType = 'exclude';
+ } else if ($scope.feed.import.source.fields && $scope.feed.import.source.fields.includes) {
+ $scope.feed.import.source.columnsType = 'include';
+ } else {
+ $scope.feed.import.source.columnsType = 'all';
+ }
+ }
+ } else {
+ $scope.dataSourceType = 'target';
+ $scope.dataTransferAction = 'load';
+ if ($scope.feed.export === undefined) {
+ $scope.feed.export = { 'target' : {
+ 'load' : {'type' : 'updateonly' }, 'columnsType' : 'all', 'fields' : {} } };
+ } else {
+ if ($scope.feed.export.target.fields && $scope.feed.export.target.fields.exlcudes) {
+ $scope.feed.export.target.columnsType = 'exclude';
+ } else if ($scope.feed.export.target.fields && $scope.feed.export.target.fields.includes) {
+ $scope.feed.export.target.columnsType = 'include';
+ } else {
+ $scope.feed.export.target.columnsType = 'all';
+ }
+ }
+ }
+ }
+ };
+ });
+
+ directivesModule.directive('feedFormHiveStorage', ['EntityFactory',function (entityFactory) {
+ return {
+ replace: false,
+ scope: {
+ storageInfo:"=",
+ add:"&",
+ show:'&',
+ toggleAdvancedOptions:'&',
+ openDatePicker:'&',
+ constructDate:'&',
+ reset:'&',
+ validations:'=',
+ required:'='
+ },
+ restrict: 'EA',
+ templateUrl: 'html/directives/feedFormHiveStorage.html',
+ link: function ($scope) {
+ $scope.valiationMessage= JSON.stringify($scope.validations.messages.number);
+ if($scope.storageInfo.type ==='target'){
+ $scope.buttonText = 'Destination';
+ }else{
+ $scope.buttonText = 'Source'
+ }
+ if(!$scope.storageInfo.clusterStorage){
+ $scope.cluster = entityFactory.newCluster($scope.storageInfo.type, 'hive', "", null);
+ }else{
+ $scope.cluster = $scope.storageInfo.clusterStorage;
+ }
+ $scope.toggleAdvancedOptions = function(){
+ $scope.showingAdvancedOptions = !$scope.showingAdvancedOptions;
+ };
+ $scope.reset = function(){
+ $scope.storageInfo.feedClusters.forEach(function (cluster, index) {
+ if (cluster.name === $scope.cluster.name && cluster.type === $scope.cluster.type) {
+ $scope.storageInfo.feedClusters[index]
+ = entityFactory.newCluster($scope.storageInfo.type, 'hive', '', null);
+ }
+ });
+ };
+ $scope.checkDuplicateClusterOnTarget = function() {
+ if ($scope.cluster.type === 'target'
+ && $scope.cluster.name !== ''
+ && $scope.storageInfo.feedClusters.filter(function (cluster) {
+ return cluster.name === $scope.cluster.name && cluster.type === 'source';
+ }).length > 0) {
+ return true;
+ }
+ return false;
+ };
+ $scope.findClusterExists = function(newClusterName, newClusterType, clusterList) {
+ $scope.clusterExists = clusterList.filter(function (cluster) {
+ return cluster.name === newClusterName && cluster.type === newClusterType;
+ }).length > 1;
+ };
+ $scope.addCluster = function(clusterDetails){
+ var cluster = entityFactory.newCluster(clusterDetails.type, clusterDetails.dataTransferType, "", null);
+ $scope.storageInfo.feedClusters.unshift(cluster);
+ //$scope.add({value : clusterDetails});
+ //$scope.reset();
+ };
+ $scope.deleteCluster = function() {
+ $scope.storageInfo.feedClusters.forEach(function (cluster, index) {
+ if (cluster.name === $scope.cluster.name && cluster.type === $scope.cluster.type) {
+ $scope.storageInfo.feedClusters.splice(index, 1);
+ }
+ });
+ };
+ }
+ };
+ }]);
+
+ directivesModule.directive('feedFormHdfsStorage', ['EntityFactory',function (entityFactory) {
+ return {
+ replace: false,
+ scope: {
+ storageInfo:"=",
+ add:"&",
+ show:'&',
+ toggleAdvancedOptions:'&',
+ openDatePicker:'&',
+ constructDate:'&',
+ reset:'&',
+ validations:'=',
+ required:'='
+ },
+ restrict: 'EA',
+ templateUrl: 'html/directives/feedFormHdfsStorage.html',
+ require:"^form",
+ link: function ($scope, $element, $attrs, $form) {
+ $scope.valiationMessage= JSON.stringify($scope.validations.messages.number);
+ if($scope.storageInfo.type ==='target'){
+ $scope.buttonText = 'Destination';
+ }else{
+ $scope.buttonText = 'Source'
+ }
+ if(!$scope.storageInfo.clusterStorage){
+ $scope.cluster = entityFactory.newCluster($scope.storageInfo.type, 'hdfs', "", null);
+ }else{
+ $scope.cluster = $scope.storageInfo.clusterStorage;
+ if (!$scope.cluster.storage.fileSystem) {
+ $scope.cluster.storage = { 'fileSystem' : entityFactory.newClusterFileSystem() };
+ }
+ }
+ $scope.toggleAdvancedOptions = function(){
+ $scope.showingAdvancedOptions = !$scope.showingAdvancedOptions;
+ };
+ $scope.reset = function(){
+ $scope.storageInfo.feedClusters.forEach(function (cluster, index) {
+ if (cluster.name === $scope.cluster.name && cluster.type === $scope.cluster.type) {
+ $scope.storageInfo.feedClusters[index]
+ = entityFactory.newCluster($scope.storageInfo.type, 'hdfs', '', null);
+ }
+ });
+ };
+ $scope.checkDuplicateClusterOnTarget = function() {
+ if ($scope.cluster.type === 'target'
+ && $scope.cluster.name !== ''
+ && $scope.storageInfo.feedClusters.filter(function (cluster) {
+ return cluster.name === $scope.cluster.name && cluster.type === 'source';
+ }).length > 0) {
+ return true;
+ }
+ return false;
+ };
+ $scope.findClusterExists = function(newClusterName, newClusterType, clusterList) {
+ $scope.clusterExists = clusterList.filter(function (cluster) {
+ return cluster.name === newClusterName && cluster.type === newClusterType;
+ }).length > 1;
+ };
+ $scope.addCluster = function(clusterDetails, feedForm){
+ var cluster = entityFactory.newCluster(clusterDetails.type, clusterDetails.dataTransferType, "", null);
+ $scope.storageInfo.feedClusters.unshift(cluster);
+ //$scope.add({value : clusterDetails});
+ //$scope.reset();
+ };
+ $scope.deleteCluster = function() {
+ $scope.storageInfo.feedClusters.forEach(function (cluster, index) {
+ if (cluster.name === $scope.cluster.name && cluster.type === $scope.cluster.type) {
+ $scope.storageInfo.feedClusters.splice(index, 1);
+ }
+ });
+ };
+ }
+ };
+ }]);
+
+ directivesModule.directive('simpleDatePicker', ['$filter','DateHelper', function ($filter, DateHelper) {
+ return {
+ require: 'ngModel',
+ link: function ($scope, $element, $attrs, ngModelController) {
+ $element.datepicker();
+
+ var dateFormat = DateHelper.getLocaleDateFormat();
+
+ $element.attr('title','Date should be entered in '+ dateFormat.toLowerCase() + ' format.');
+
+ ngModelController.$parsers.push(function (date) {
+ //convert data from view format to model format
+ return new Date(date);
+ });
+ ngModelController.$formatters.push(function (date) {
+ //convert data from model format to view format
+ if (date !== "") {
+ date = $filter('date')(date, dateFormat);
+ }
+ return date;
+ });
+ }
+ };
+ }]);
+
+ directivesModule.directive('mandatoryField', function () {
+ return {
+ replace: false,
+ restrict: 'E',
+ template: '<span>*</span>'
+ };
+ });
+
+}());
http://git-wip-us.apache.org/repos/asf/falcon/blob/76dc2e18/falcon-ui/app/js/directives/entities-search-list.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/directives/entities-search-list.js b/falcon-ui/app/js/directives/entities-search-list.js
index 3e76f63..9171fbd 100644
--- a/falcon-ui/app/js/directives/entities-search-list.js
+++ b/falcon-ui/app/js/directives/entities-search-list.js
@@ -20,8 +20,16 @@
var entitiesListModule = angular.module('app.directives.entities-search-list', ['app.services' ]);
- entitiesListModule.controller('EntitiesSearchListCtrl', ['$scope', 'Falcon', 'X2jsService', '$window', 'EncodeService',
- function($scope, Falcon, X2jsService, $window, encodeService) {
+ entitiesListModule.controller('EntitiesSearchListCtrl', ['$scope', 'Falcon', 'X2jsService', '$window', 'EncodeService', '$rootScope',
+ function($scope, Falcon, X2jsService, $window, encodeService, $rootScope) {
+
+ $scope.isSafeMode = function() {
+ return $rootScope.safeMode;
+ };
+
+ $scope.isSuperUser = function() {
+ return $rootScope.superUser;
+ };
$scope.downloadEntity = function(type, name) {
Falcon.logRequest();
@@ -47,7 +55,7 @@
};
});
- entitiesListModule.directive('entitiesSearchList', ["$timeout", 'Falcon', function($timeout, Falcon) {
+ entitiesListModule.directive('entitiesSearchList', ["$timeout", 'Falcon', "$state", "$rootScope", function($timeout, Falcon, $state, $rootScope) {
return {
scope: {
input: "=",
@@ -78,7 +86,7 @@
}, true);
scope.selectedRows = [];
- scope.mirrorTag = "_falcon_mirroring_type";
+ scope.mirrorTag = "_falcon_extension_name";
scope.checkedRow = function (name) {
var isInArray = false;
@@ -159,6 +167,17 @@
scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:true };
}
+ var selectedClusterRows = scope.selectedRows.filter(function(entity) {
+ return entity.type == 'cluster' || entity.type == 'CLUSTER';
+ });
+ if (selectedClusterRows && selectedClusterRows.length > 0) {
+ scope.selectedDisabledButtons = {
+ schedule:true,
+ suspend:true,
+ resume:true
+ };
+ }
+
if(scope.selectedRows.length === 0) {
scope.selectedDisabledButtons = {
schedule:true,
@@ -166,6 +185,7 @@
resume:true
};
}
+
}, 50);
};
@@ -209,16 +229,48 @@
};
scope.scopeEdit = function () {
- scope.edit(scope.selectedRows[0].type, scope.selectedRows[0].name);
+ var selectedRow = scope.selectedRows[0];
+ if (selectedRow.type.toLowerCase() === 'cluster' && (!$rootScope.safeMode || !$rootScope.superUser)) {
+ return;
+ }
+ var state = 'forms.' + selectedRow.type.toLowerCase();
+ var selectedEntity = scope.input.filter(function(value){
+ return value.name === selectedRow.name;
+ });
+ if(selectedRow.type.toLowerCase() === 'process' && selectedEntity[0].tags
+ && scope.isMirror(selectedEntity[0].tags.tag)){
+ var mirrorType = scope.getMirrorType(selectedEntity[0].tags.tag);
+ if (mirrorType === 'hdfs-mirror' || mirrorType === 'hive-mirror') {
+ state = 'forms.dataset';
+ } else {
+ state = 'forms.snapshot';
+ }
+ }
+ $state.go(state, {'name' : selectedRow.name, 'action' : 'edit'});
};
+
scope.scopeClone = function () {
- scope.clone(scope.selectedRows[0].type, scope.selectedRows[0].name);
+ var selectedRow = scope.selectedRows[0];
+ var state = 'forms.' + selectedRow.type.toLowerCase();
+ var selectedEntity = scope.input.filter(function(value){
+ return value.name === selectedRow.name;
+ });
+ if(selectedRow.type.toLowerCase() === 'process' && selectedEntity[0].tags
+ && scope.isMirror(selectedEntity[0].tags.tag)){
+ var mirrorType = scope.getMirrorType(selectedEntity[0].tags.tag);
+ if (mirrorType === 'hdfs-mirror' || mirrorType === 'hive-mirror') {
+ state = 'forms.dataset';
+ } else {
+ state = 'forms.snapshot';
+ }
+ }
+ $state.go(state, {'name' : selectedRow.name, 'action' : 'clone'});
};
scope.goEntityDefinition = function(name, type) {
scope.entityDefinition(name, type);
};
scope.goEntityDetails = function(name, type) {
- scope.entityDetails(name, type);
+ $state.go('entityDetails',{'name' : name, 'type' : type});
};
scope.scopeRemove = function () {
@@ -279,19 +331,31 @@
return flag;
};
+ scope.getMirrorType = function(tags) {
+ if (tags.indexOf('_falcon_extension_name=HDFS-MIRRORING') !== -1) {
+ return "hdfs-mirror";
+ } else if (tags.indexOf('_falcon_extension_name=HDFS-SNAPSHOT-MIRRORING') !== -1) {
+ return "snapshot";
+ } else if (tags.indexOf('_falcon_extension_name=HIVE-MIRRORING') !== -1) {
+ return "hive-mirror";
+ }
+ };
+
scope.displayIcon = function (type, tags) {
if(type === "FEED"){
return "entypo download";
- }else if(type === "PROCESS" && scope.isMirror(tags)){
+ } else if(type === "CLUSTER"){
+ return "entypo archive";
+ } else if(type === "PROCESS" && scope.isMirror(tags)){
return "glyphicon glyphicon-duplicate";
- }else{
+ } else{
return "entypo cycle";
}
};
scope.displayType = function (tag) {
var tagKeyVal = tag.split("=");
- if(tagKeyVal[0] === "_falcon_mirroring_type"){
+ if(tagKeyVal[0] === "_falcon_extension_name"){
return tagKeyVal[1];
}else{
return "";
@@ -302,4 +366,4 @@
};
}]);
-})();
\ No newline at end of file
+})();
http://git-wip-us.apache.org/repos/asf/falcon/blob/76dc2e18/falcon-ui/app/js/directives/feed-cluster-partitions.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/directives/feed-cluster-partitions.js b/falcon-ui/app/js/directives/feed-cluster-partitions.js
new file mode 100644
index 0000000..8fadb90
--- /dev/null
+++ b/falcon-ui/app/js/directives/feed-cluster-partitions.js
@@ -0,0 +1,119 @@
+/**
+ * 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.
+ */
+(function () {
+ 'use strict';
+
+ var module = angular.module('app.directives.feed-cluster-partitions', []);
+
+ module.directive('feedFormClusterPartitions', function () {
+ return {
+ replace: false,
+ restrict: 'EA',
+ templateUrl: 'html/feed/feedFormClusterPartitionsTpl.html',
+ link: function ($scope, $element) {
+ function addFeedPartitionsIfNotExists(partitions, existingPartitions) {
+ var runningPartition;
+ for (var i=0;i<partitions.length;i++) {
+ runningPartition = partitions[i];
+ if (runningPartition.trim() != "") {
+ var matchedPartitions = existingPartitions.filter(function(element) {
+ return element.partitionName === runningPartition;
+ });
+ if (matchedPartitions.length < 1) {
+ existingPartitions.splice(i, 0, {"partitionName" : runningPartition,
+ "partitionText" : "",
+ "mapping" : "any"});
+ }
+ }
+ }
+ }
+
+ function removeClusterPartitionsIfNotExists(existingPartitions, partitions) {
+ var runningPartition;
+ var updatedExistingParititons = [];
+ for (var i=0;i<existingPartitions.length;i++) {
+ runningPartition = existingPartitions[i];
+ var matchedPartitions = partitions.filter(function(element) {
+ return element === runningPartition.partitionName;
+ });
+ if (matchedPartitions.length > 0) {
+ updatedExistingParititons.push(runningPartition);
+ }
+ }
+ return updatedExistingParititons;
+ }
+
+ function createDefaultClusterPartitions() {
+ var currentPartitions = ($scope.feedPartitions.trim() === "") ? [] : $scope.feedPartitions.split(",");
+ addFeedPartitionsIfNotExists(currentPartitions, $scope.partitionList);
+ $scope.partitionList = removeClusterPartitionsIfNotExists($scope.partitionList, currentPartitions);
+ }
+
+ function createSpecificClusterPartitions() {
+ var partitionExpressions = $scope.cluster.partition.split("/");
+ var currentPartitions = ($scope.feedPartitions.trim() === "") ? [] : $scope.feedPartitions.split(",");
+ partitionExpressions.forEach(function(partitionExpression, index) {
+ $scope.partitionList.push({"partitionName" : currentPartitions[index],
+ "partitionText" : (partitionExpression == "*") ? "" : partitionExpression,
+ "mapping" : (partitionExpression == "*") ? "any" : "mappedPartition"
+ });
+ });
+ }
+
+ $scope.preparePartitionExpression = function(partitionList) {
+ var partitionExpression = "";
+ for(var i=0;i<partitionList.length;i++) {
+ var partition = partitionList[i];
+ if (partition.mapping === 'any') {
+ partitionExpression = partitionExpression.concat("*");
+ } else if (partition.mapping === 'mappedPartition') {
+ partitionExpression = partitionExpression.concat(partition.partitionText);
+ }
+ if (i < partitionList.length-1) {
+ partitionExpression = partitionExpression.concat("/");
+ }
+ }
+ $scope.cluster.partition = partitionExpression;
+ }
+
+ $scope.partitionList = [];
+ if ($scope.cluster.partition != undefined) {
+ $scope.selectPartition = true;
+ createSpecificClusterPartitions()
+ } else {
+ $scope.selectPartition = false;
+ createDefaultClusterPartitions();
+ }
+
+ $scope.$watch('selectPartition', function() {
+ if ($scope.selectPartition) {
+ $scope.preparePartitionExpression($scope.partitionList);
+ } else {
+ delete $scope.cluster.partition;
+ }
+ });
+
+ $scope.$on('feed:createClusterPartitions', function() {
+ createDefaultClusterPartitions();
+ $scope.preparePartitionExpression($scope.partitionList);
+ });
+ }
+ };
+ });
+
+})();