You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@falcon.apache.org by so...@apache.org on 2015/10/13 02:04:01 UTC
[08/22] falcon git commit: FALCON-1315 Update falcon ui for HiveDR,
secure clusters and bug fixes. Contributed by Armando Reyna/Venkat
Ranganathan.
http://git-wip-us.apache.org/repos/asf/falcon/blob/86180d93/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
new file mode 100644
index 0000000..c8faa74
--- /dev/null
+++ b/falcon-ui/app/js/directives/dependencies-graph.js
@@ -0,0 +1,294 @@
+/**
+ * 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 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.directive('dependenciesGraph', ["$timeout", 'Falcon', '$filter', '$state', 'X2jsService', 'EntityModel',
+ function($timeout, Falcon, $filter, $state, X2jsService, EntityModel) {
+ return {
+ scope: {
+ type: "=",
+ name: "="
+ },
+ controller: 'DependenciesGraphCtrl',
+ restrict: "EA",
+ templateUrl: 'html/directives/dependenciesGraphDv.html',
+ link: function (scope, element) {
+
+ var loadDependencyGraph = function(entity_type, entity_name, done_callback) {
+ var nodes = {};
+ var next_node_id = 0;
+
+ var requests_in_fly = 0;
+
+ function key(type, name) {
+ return type + '/' + name;
+ }
+
+ function getOrCreateNode(type, name) {
+ var k = key(type, name);
+ if (nodes[k] !== undefined)
+ return nodes[k];
+
+ var n = {
+ "id": next_node_id++,
+ "type": type,
+ "name": name,
+ "dependency": []
+ };
+ nodes[k] = n;
+ return n;
+ }
+
+ function loadEntry(node) {
+ var type = node.type, name = node.name, k = key(type, name);
+
+ Falcon.logRequest();
+ Falcon.getEntityDependencies(type, name)
+ .success(function (data) {
+ Falcon.logResponse('success', data, false, true);
+
+ if (data.entity == null)
+ return;
+
+ if (!($.isArray(data.entity)))
+ data.entity = new Array(data.entity);
+
+ 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 src = null, dst = null;
+ if (d.type === "cluster") {
+ src = node; dst = d;
+ } else if (d.type === "process") {
+ src = d; dst = node;
+ } else {
+ if (node.type === "cluster") {
+ src = d; dst = node;
+ } else {
+ src = node; dst = d;
+ }
+ }
+ //console.log(src.name + '->' + dst.name);
+ src.dependency.push(dst.id);
+ }
+
+ done_callback(nodes);
+ })
+ .error(function (err) {
+ Falcon.logResponse('error', err, false, true);
+ });
+ }
+
+ function load() {
+ var n = getOrCreateNode(entity_type, entity_name);
+ 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");
+
+ // 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 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);
+ });
+
+
+ });
+
+ }
+
+ 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);
+ });
+ }
+
+ 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');
+ }
+
+ function plot() {
+ var g = new dagre.Digraph();
+
+ 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 });
+ }
+
+ 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);
+ }
+ }
+
+ layout = dagre.layout()
+ .universalSep(UNIVERSAL_SEP).rankSep(SEPARATION)
+ .run(g);
+ layout.eachEdge(drawEdge);
+ layout.eachNode(drawNode);
+
+ var graph = layout.graph();
+
+ 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]);
+ });
+ };
+
+ //console.log(scope.type + " " + scope.name);
+ visualizeDependencyGraph(scope.type, scope.name);
+
+ }
+ };
+ }]);
+
+})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/falcon/blob/86180d93/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 c839269..9c388ea 100644
--- a/falcon-ui/app/js/directives/directives.js
+++ b/falcon-ui/app/js/directives/directives.js
@@ -20,11 +20,16 @@
var directivesModule = angular.module('app.directives', [
'app.services',
- 'app.directives.entities-list',
+ //'app.directives.entities-list',
+ 'app.directives.entities-search-list',
+ 'app.directives.instances-list',
'app.directives.server-messages',
'app.directives.entity',
'app.directives.check-name',
- 'app.directives.validation-message'
+ 'app.directives.validation-message',
+ 'chart-module',
+ 'app.directives.dependencies-graph',
+ 'app.directives.lineage-graph'
]);
directivesModule.directive('navHeader', function () {
@@ -88,4 +93,90 @@
};
});
+ directivesModule.directive('simpleDate', ['$filter', function ($filter) {
+ return {
+ require: 'ngModel',
+ link: function (scope, element, attrs, ngModelController) {
+ ngModelController.$parsers.push(function (data) {
+ //convert data from view format to model format
+ return data;
+ });
+ ngModelController.$formatters.push(function (date) {
+ //convert data from model format to view format
+ if (date !== "") {
+ date = $filter('date')(date, 'MM/dd/yyyy');
+ }
+ return date;
+ });
+ }
+ };
+ }]);
+
+ directivesModule.directive('ngEnter', function () {
+ return function (scope, element, attrs) {
+ element.bind("keydown keypress", function (event) {
+ if (event.which === 13) {
+ scope.$apply(function () {
+ scope.$eval(attrs.ngEnter);
+ });
+ event.preventDefault();
+ }
+ });
+ };
+ });
+
+ directivesModule.directive('elastic', ['$timeout', function ($timeout) {
+ return {
+ restrict: 'A',
+ link: function ($scope, element) {
+ $scope.$watch(function () {
+ return element[0].value;
+ }, function () {
+ resize();
+ });
+ var resize = function () {
+ element[0].style.height = "250px";
+ return element[0].style.height = "" + element[0].scrollHeight + "px";
+ };
+ $timeout(resize, 0);
+ }
+ };
+ }
+ ]);
+
+ directivesModule.directive('autofocus', ['$timeout', function ($timeout) {
+ return {
+ restrict: 'A',
+ link: function ($scope, element) {
+ $timeout(function () {
+ element.trigger('focus');
+ }, 20);
+ }
+ };
+ }
+ ]);
+
+ directivesModule.filter('dateFormatter', function () {
+ return function (date) {
+ console.log(date);
+ var dates = date.split('T')[0],
+ time = date.split('T')[1].split('Z')[0].split('.')[0];
+ return dates + ' ' + time;
+ };
+ });
+
+ directivesModule.directive('onBlur', [function () {
+ return {
+ restrict: 'A',
+ link: function (scope, elm, attrs) {
+ elm.bind('blur', function () {
+ if (attrs.onBlur)
+ scope[attrs.onBlur]();
+ else
+ return false;
+ });
+ }
+ };
+ }]);
+
}());
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/falcon/blob/86180d93/falcon-ui/app/js/directives/entities-list.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/directives/entities-list.js b/falcon-ui/app/js/directives/entities-list.js
index aeced2f..065806a 100644
--- a/falcon-ui/app/js/directives/entities-list.js
+++ b/falcon-ui/app/js/directives/entities-list.js
@@ -19,10 +19,10 @@
'use strict';
var entitiesListModule = angular.module('app.directives.entities-list', ['app.services' ]);
-
+
entitiesListModule.controller('EntitiesListCtrl', ['$scope', 'Falcon', 'X2jsService', '$window', 'EncodeService',
function($scope, Falcon, X2jsService, $window, encodeService) {
-
+
$scope.downloadEntity = function(type, name) {
Falcon.logRequest();
Falcon.getEntityDefinition(type, name) .success(function (data) {
@@ -32,26 +32,26 @@
Falcon.logResponse('error', err, false);
});
};
-
+
}]);
-
+
entitiesListModule.filter('tagFilter', function () {
return function (items) {
var filtered = [], i;
for (i = 0; i < items.length; i++) {
var item = items[i];
if(!item.list || !item.list.tag) { item.list = {tag:[""]}; }
- filtered.push(item);
+ filtered.push(item);
}
return filtered;
};
});
-
+
entitiesListModule.directive('entitiesList', ["$timeout", 'Falcon', function($timeout, Falcon) {
return {
scope: {
input: "=",
- schedule: "=",
+ schedule: "=",
suspend: "=",
clone: "=",
remove: "=",
@@ -73,6 +73,15 @@
}, true);
scope.selectedRows = [];
+ scope.checkedRow = function (name) {
+ var isInArray = false;
+ scope.selectedRows.forEach(function(item) {
+ if (name === item.name) {
+ isInArray = true;
+ }
+ });
+ return isInArray;
+ };
scope.simpleFilter = {};
scope.selectedDisabledButtons = {
schedule:true,
@@ -131,10 +140,10 @@
};
scope.scopeEdit = function () {
- scope.edit(scope.selectedRows[0].type, scope.selectedRows[0].name);
+ scope.edit(scope.selectedRows[0].type, scope.selectedRows[0].name);
};
scope.scopeClone = function () {
- scope.clone(scope.selectedRows[0].type, scope.selectedRows[0].name);
+ scope.clone(scope.selectedRows[0].type, scope.selectedRows[0].name);
};
scope.goEntityDetails = function(name, type) {
scope.entityDetails(name, type);
@@ -142,18 +151,18 @@
scope.scopeRemove = function () {
var i;
- for(i = 0; i < scope.selectedRows.length; i++) {
- var multiRequestType = scope.selectedRows[i].type.toLowerCase();
- Falcon.responses.multiRequest[multiRequestType] += 1;
- scope.remove(scope.selectedRows[i].type, scope.selectedRows[i].name);
+ for(i = 0; i < scope.selectedRows.length; i++) {
+ var multiRequestType = scope.selectedRows[i].type.toLowerCase();
+ Falcon.responses.multiRequest[multiRequestType] += 1;
+ scope.remove(scope.selectedRows[i].type, scope.selectedRows[i].name);
}
};
scope.scopeSchedule = function () {
- var i;
+ var i;
for(i = 0; i < scope.selectedRows.length; i++) {
var multiRequestType = scope.selectedRows[i].type.toLowerCase();
- Falcon.responses.multiRequest[multiRequestType] += 1;
+ Falcon.responses.multiRequest[multiRequestType] += 1;
scope.schedule(scope.selectedRows[i].type, scope.selectedRows[i].name);
}
};
@@ -162,7 +171,7 @@
var i;
for(i = 0; i < scope.selectedRows.length; i++) {
var multiRequestType = scope.selectedRows[i].type.toLowerCase();
- Falcon.responses.multiRequest[multiRequestType] += 1;
+ Falcon.responses.multiRequest[multiRequestType] += 1;
scope.suspend(scope.selectedRows[i].type, scope.selectedRows[i].name);
}
};
@@ -170,20 +179,20 @@
var i;
for(i = 0; i < scope.selectedRows.length; i++) {
var multiRequestType = scope.selectedRows[i].type.toLowerCase();
- Falcon.responses.multiRequest[multiRequestType] += 1;
+ Falcon.responses.multiRequest[multiRequestType] += 1;
scope.resume(scope.selectedRows[i].type, scope.selectedRows[i].name);
}
};
scope.download = function() {
var i;
- for(i = 0; i < scope.selectedRows.length; i++) {
+ for(i = 0; i < scope.selectedRows.length; i++) {
scope.downloadEntity(scope.selectedRows[i].type, scope.selectedRows[i].name);
}
};
-
+
}
};
}]);
-
+
})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/falcon/blob/86180d93/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
new file mode 100644
index 0000000..3e76f63
--- /dev/null
+++ b/falcon-ui/app/js/directives/entities-search-list.js
@@ -0,0 +1,305 @@
+/**
+ * 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 entitiesListModule = angular.module('app.directives.entities-search-list', ['app.services' ]);
+
+ entitiesListModule.controller('EntitiesSearchListCtrl', ['$scope', 'Falcon', 'X2jsService', '$window', 'EncodeService',
+ function($scope, Falcon, X2jsService, $window, encodeService) {
+
+ $scope.downloadEntity = function(type, name) {
+ Falcon.logRequest();
+ Falcon.getEntityDefinition(type, name) .success(function (data) {
+ Falcon.logResponse('success', data, false, true);
+ $window.location.href = 'data:application/octet-stream,' + encodeService.encode(data);
+ }).error(function (err) {
+ Falcon.logResponse('error', err, false);
+ });
+ };
+
+ }]);
+
+ entitiesListModule.filter('tagFilter', function () {
+ return function (items) {
+ var filtered = [], i;
+ for (i = 0; i < items.length; i++) {
+ var item = items[i];
+ if(!item.list || !item.list.tag) { item.list = {tag:[""]}; }
+ filtered.push(item);
+ }
+ return filtered;
+ };
+ });
+
+ entitiesListModule.directive('entitiesSearchList', ["$timeout", 'Falcon', function($timeout, Falcon) {
+ return {
+ scope: {
+ input: "=",
+ schedule: "=",
+ suspend: "=",
+ clone: "=",
+ remove: "=",
+ edit: "=",
+ type: "@",
+ tags: "=",
+ focusSearch: "=",
+ entityDetails:"=",
+ entityDefinition:"=",
+ resume:"=",
+ refresh: "=",
+ pages: "=",
+ goPage: "="
+ },
+ controller: 'EntitiesSearchListCtrl',
+ restrict: "EA",
+ templateUrl: 'html/directives/entitiesSearchListDv.html',
+ link: function (scope) {
+ scope.server = Falcon;
+ scope.$watch('input', function() {
+ scope.selectedRows = [];
+ scope.checkButtonsToShow();
+
+ }, true);
+
+ scope.selectedRows = [];
+ scope.mirrorTag = "_falcon_mirroring_type";
+
+ scope.checkedRow = function (name) {
+ var isInArray = false;
+ scope.selectedRows.forEach(function(item) {
+ if (name === item.name) {
+ isInArray = true;
+ }
+ });
+ return isInArray;
+ };
+
+ var nameOrder = true;
+ scope.toggleSortOrder = function () {
+ Falcon.orderBy.enable = true;
+ if (nameOrder) {
+ Falcon.orderBy.name = 'asc';
+ } else {
+ Falcon.orderBy.name = 'desc';
+ }
+ nameOrder = !nameOrder;
+ scope.$parent.refreshList(scope.$parent.tags);
+
+ };
+
+ scope.simpleFilter = {};
+
+ scope.selectedDisabledButtons = {
+ schedule:true,
+ suspend:true,
+ resume:true
+ };
+
+ scope.checkButtonsToShow = function() {
+ var statusCount = {
+ "SUBMITTED":0,
+ "RUNNING":0,
+ "SUSPENDED":0,
+ "UNKNOWN":0
+ };
+
+ $timeout(function() {
+
+ if(scope.selectedRows.length === scope.input.length){
+ scope.selectedAll = true;
+ }else{
+ scope.selectedAll = false;
+ }
+
+ scope.selectedRows.forEach(function(entity) {
+ statusCount[entity.status] = statusCount[entity.status]+1;
+ });
+
+ if(statusCount.SUBMITTED > 0) {
+ if(statusCount.RUNNING > 0 || statusCount.SUSPENDED > 0 || statusCount.UNKNOWN > 0) {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:true };
+ }
+ else {
+ scope.selectedDisabledButtons = { schedule:false, suspend:true, resume:true };
+ }
+ }
+ if(statusCount.RUNNING > 0) {
+ if(statusCount.SUBMITTED > 0 || statusCount.SUSPENDED > 0 || statusCount.UNKNOWN > 0) {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:true };
+ }
+ else {
+ scope.selectedDisabledButtons = { schedule:true, suspend:false, resume:true };
+ }
+ }
+ if (statusCount.SUSPENDED > 0) {
+ if(statusCount.SUBMITTED > 0 || statusCount.RUNNING > 0 || statusCount.UNKNOWN > 0) {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:true };
+ }
+ else {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:false };
+ }
+ }
+ if (statusCount.UNKNOWN > 0) {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:true };
+ }
+
+ if(scope.selectedRows.length === 0) {
+ scope.selectedDisabledButtons = {
+ schedule:true,
+ suspend:true,
+ resume:true
+ };
+ }
+ }, 50);
+ };
+
+ var isSelected = function(item){
+ var selected = false;
+ scope.selectedRows.forEach(function(entity) {
+ if(angular.equals(item, entity)){
+ selected = true;
+ }
+ });
+ return selected;
+ };
+
+ scope.checkAll = function () {
+ if(scope.selectedRows.length === scope.input.length){
+ angular.forEach(scope.input, function (item) {
+ scope.selectedRows.pop();
+ });
+ }else{
+ angular.forEach(scope.input, function (item) {
+ var checkbox = {name:item.name, type:item.type, status:item.status};
+ if(!isSelected(checkbox)){
+ scope.selectedRows.push(checkbox);
+ }
+ });
+ }
+ };
+
+ scope.addTag = function(text){
+ var added = false;
+ angular.forEach(scope.tags, function (scopeTag) {
+ if(scopeTag.text === text){
+ added = true;
+ }
+ });
+ if(!added){
+ var tag = {text:"Tag:"+text};
+ scope.tags.push(tag);
+ scope.focusSearch();
+ }
+ };
+
+ scope.scopeEdit = function () {
+ scope.edit(scope.selectedRows[0].type, scope.selectedRows[0].name);
+ };
+ scope.scopeClone = function () {
+ scope.clone(scope.selectedRows[0].type, scope.selectedRows[0].name);
+ };
+ scope.goEntityDefinition = function(name, type) {
+ scope.entityDefinition(name, type);
+ };
+ scope.goEntityDetails = function(name, type) {
+ scope.entityDetails(name, type);
+ };
+
+ scope.scopeRemove = function () {
+ var i;
+ for(i = 0; i < scope.selectedRows.length; i++) {
+ var multiRequestType = scope.selectedRows[i].type.toLowerCase();
+ Falcon.responses.multiRequest[multiRequestType] += 1;
+ scope.remove(scope.selectedRows[i].type, scope.selectedRows[i].name);
+ }
+ };
+
+ scope.scopeSchedule = function () {
+ var i;
+ for(i = 0; i < scope.selectedRows.length; i++) {
+ var multiRequestType = scope.selectedRows[i].type.toLowerCase();
+ Falcon.responses.multiRequest[multiRequestType] += 1;
+ scope.schedule(scope.selectedRows[i].type, scope.selectedRows[i].name);
+ }
+ };
+
+ scope.scopeSuspend = function () {
+ var i;
+ for(i = 0; i < scope.selectedRows.length; i++) {
+ var multiRequestType = scope.selectedRows[i].type.toLowerCase();
+ Falcon.responses.multiRequest[multiRequestType] += 1;
+ scope.suspend(scope.selectedRows[i].type, scope.selectedRows[i].name);
+ }
+ };
+ scope.scopeResume = function () {
+ var i;
+ for(i = 0; i < scope.selectedRows.length; i++) {
+ var multiRequestType = scope.selectedRows[i].type.toLowerCase();
+ Falcon.responses.multiRequest[multiRequestType] += 1;
+ scope.resume(scope.selectedRows[i].type, scope.selectedRows[i].name);
+ }
+ };
+
+ scope.download = function() {
+ var i;
+ for(i = 0; i < scope.selectedRows.length; i++) {
+ scope.downloadEntity(scope.selectedRows[i].type, scope.selectedRows[i].name);
+ }
+ };
+
+ scope.scopeGoPage = function (page) {
+ scope.goPage(page);
+ };
+
+ scope.isMirror = function(tags){
+ var flag = false;
+ if(tags !== undefined){
+ tags.forEach(function(tag) {
+ if(tag.indexOf(scope.mirrorTag) !== -1){
+ flag = true;
+ }
+ });
+ }
+ return flag;
+ };
+
+ scope.displayIcon = function (type, tags) {
+ if(type === "FEED"){
+ return "entypo download";
+ }else if(type === "PROCESS" && scope.isMirror(tags)){
+ return "glyphicon glyphicon-duplicate";
+ }else{
+ return "entypo cycle";
+ }
+ };
+
+ scope.displayType = function (tag) {
+ var tagKeyVal = tag.split("=");
+ if(tagKeyVal[0] === "_falcon_mirroring_type"){
+ return tagKeyVal[1];
+ }else{
+ return "";
+ }
+ };
+
+ }
+ };
+ }]);
+
+})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/falcon/blob/86180d93/falcon-ui/app/js/directives/instances-list.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/directives/instances-list.js b/falcon-ui/app/js/directives/instances-list.js
new file mode 100644
index 0000000..269c7c1
--- /dev/null
+++ b/falcon-ui/app/js/directives/instances-list.js
@@ -0,0 +1,832 @@
+/**
+ * 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 entitiesListModule = angular.module('app.directives.instances-list', ['app.services' ]);
+
+ entitiesListModule.controller('InstancesListCtrl', ['$scope', 'Falcon', 'X2jsService', '$window', 'EncodeService',
+ function($scope, Falcon, X2jsService, $window, encodeService) {
+
+ //$scope.downloadEntity = function(logURL) {
+ // Falcon.logRequest();
+ // Falcon.getInstanceLog(logURL) .success(function (data) {
+ // Falcon.logResponse('success', data, false, true);
+ // $window.location.href = 'data:application/octet-stream,' + encodeService.encode(data);
+ // }).error(function (err) {
+ // Falcon.logResponse('error', err, false);
+ // });
+ //};
+
+ $scope.downloadEntity = function(logURL) {
+ $window.location.href = logURL;
+ };
+
+ }]);
+
+ entitiesListModule.filter('tagFilter', function () {
+ return function (items) {
+ var filtered = [], i;
+ for (i = 0; i < items.length; i++) {
+ var item = items[i];
+ if(!item.list || !item.list.tag) { item.list = {tag:[""]}; }
+ filtered.push(item);
+ }
+ return filtered;
+ };
+ });
+
+ entitiesListModule.directive('instancesList', ["$timeout", 'Falcon', '$filter', function($timeout, Falcon, $filter) {
+ return {
+ scope: {
+ input: "=",
+ type: "=",
+ name: "=",
+ start: "=",
+ end: "=",
+ instanceDetails:"=",
+ refresh: "=",
+ pages: "=",
+ nextPages: "=",
+ prevPages: "=",
+ goPage: "=",
+ changePagesSet: "="
+ },
+ controller: 'InstancesListCtrl',
+ restrict: "EA",
+ templateUrl: 'html/directives/instancesListDv.html',
+ link: function (scope) {
+ scope.server = Falcon;
+ scope.$watch(function () { return scope.input; }, function() {
+ scope.selectedRows = [];
+ scope.checkButtonsToShow();
+ }, true);
+
+ var resultsPerPage = 10;
+ var visiblePages = 3;
+ scope.selectedRows = [];
+ scope.$parent.refreshInstanceList(scope.type, scope.name, scope.start, scope.end);
+
+ scope.startSortOrder = "desc";
+ scope.endSortOrder = "desc";
+ scope.statusSortOrder = "desc";
+
+ scope.checkedRow = function (name) {
+ var isInArray = false;
+ scope.selectedRows.forEach(function(item) {
+ if (name === item.instance) {
+ isInArray = true;
+ }
+ });
+ return isInArray;
+ };
+
+ scope.simpleFilter = {};
+
+ scope.selectedDisabledButtons = {
+ schedule:true,
+ suspend:true,
+ resume:true,
+ stop:true
+ };
+
+ scope.checkButtonsToShow = function() {
+ var statusCount = {
+ "SUBMITTED":0,
+ "RUNNING":0,
+ "SUSPENDED":0,
+ "UNKNOWN":0,
+ "KILLED":0,
+ "WAITING":0,
+ "FAILED":0,
+ "SUCCEEDED":0
+ };
+
+ $timeout(function() {
+
+ if(scope.selectedRows.length === scope.input.length){
+ scope.selectedAll = true;
+ }else{
+ scope.selectedAll = false;
+ }
+
+ scope.selectedRows.forEach(function(instance) {
+ statusCount[instance.status] = statusCount[instance.status]+1;
+ });
+
+ if(statusCount.SUBMITTED > 0) {
+ if(statusCount.RUNNING > 0 || statusCount.SUSPENDED > 0 || statusCount.UNKNOWN > 0 || statusCount.KILLED > 0 || statusCount.WAITING > 0 || statusCount.FAILED > 0) {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:true, stop:true, rerun:true };
+ }
+ else {
+ scope.selectedDisabledButtons = { schedule:false, suspend:true, resume:true, stop:true, rerun:true };
+ }
+ }
+ if(statusCount.RUNNING > 0) {
+ if(statusCount.SUBMITTED > 0 || statusCount.SUSPENDED > 0 || statusCount.UNKNOWN > 0 || statusCount.KILLED > 0 || statusCount.WAITING > 0 || statusCount.FAILED > 0) {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:true, stop:false, rerun:true };
+ }
+ else {
+ scope.selectedDisabledButtons = { schedule:true, suspend:false, resume:true, stop:false, rerun:true };
+ }
+ }
+ if (statusCount.SUSPENDED > 0) {
+ if(statusCount.SUBMITTED > 0 || statusCount.RUNNING > 0 || statusCount.UNKNOWN > 0 || statusCount.KILLED > 0 || statusCount.WAITING > 0 || statusCount.FAILED > 0) {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:true, stop:false, rerun:true };
+ }
+ else {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:false, stop:false, rerun:true };
+ }
+ }
+ if (statusCount.KILLED > 0) {
+ if(statusCount.SUBMITTED > 0 || statusCount.SUSPENDED > 0 || statusCount.RUNNING > 0 || statusCount.UNKNOWN > 0 || statusCount.WAITING > 0 || statusCount.FAILED > 0) {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:true, stop:true, rerun:true };
+ }
+ else {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:true, stop:true, rerun:false };
+ }
+ }
+ if(statusCount.WAITING > 0) {
+ if(statusCount.SUBMITTED > 0 || statusCount.RUNNING > 0 || statusCount.SUSPENDED > 0 || statusCount.UNKNOWN > 0 || statusCount.KILLED > 0 || statusCount.FAILED > 0) {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:true, stop:true, rerun:true };
+ }
+ else {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:true, stop:true, rerun:true };
+ }
+ }
+ if (statusCount.FAILED > 0) {
+ if(statusCount.SUBMITTED > 0 || statusCount.SUSPENDED > 0 || statusCount.RUNNING > 0 || statusCount.UNKNOWN > 0 || statusCount.KILLED > 0 || statusCount.WAITING > 0) {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:true, stop:true, rerun:true };
+ }
+ else {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:true, stop:true, rerun:false };
+ }
+ }
+ if(statusCount.SUCCEEDED > 0) {
+ if(statusCount.SUBMITTED > 0 || statusCount.RUNNING > 0 || statusCount.SUSPENDED > 0 || statusCount.UNKNOWN > 0 || statusCount.KILLED > 0 || statusCount.WAITING > 0 || statusCount.FAILED > 0) {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:true, stop:true, rerun:true };
+ }
+ else {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:true, stop:true, rerun:false };
+ }
+ }
+ if (statusCount.UNKNOWN > 0) {
+ scope.selectedDisabledButtons = { schedule:true, suspend:true, resume:true, stop:true, rerun:true };
+ }
+
+ if(scope.selectedRows.length === 0) {
+ scope.selectedDisabledButtons = {
+ schedule:true,
+ resume:true,
+ suspend:true,
+ stop:true,
+ rerun:true
+ };
+ }
+ }, 50);
+ };
+
+ var isSelected = function(item){
+ var selected = false;
+ scope.selectedRows.forEach(function(entity) {
+ if(angular.equals(item, entity)){
+ selected = true;
+ }
+ });
+ return selected;
+ }
+
+ scope.checkAll = function () {
+ if(scope.selectedRows.length >= scope.input.length){
+ angular.forEach(scope.input, function (item) {
+ scope.selectedRows.pop();
+ });
+ }else{
+ angular.forEach(scope.input, function (item) {
+ var checkbox = {'instance':item.instance, 'startTime':item.startTime, 'endTime':item.endTime, 'status':item.status, 'type':scope.type, 'logFile':item.logFile};
+ if(!isSelected(checkbox)){
+ scope.selectedRows.push(checkbox);
+ }
+ });
+ }
+ };
+
+ scope.goInstanceDetails = function(instance) {
+ scope.instanceDetails(instance);
+ };
+
+ var resumeInstance = function (type, name, start, end, refresh) {
+ Falcon.logRequest();
+ Falcon.postResumeInstance(type, name, start, end)
+ .success(function (message) {
+ Falcon.logResponse('success', message, type);
+ if(refresh){
+ scope.$parent.refreshInstanceList(scope.type, scope.name, scope.start, scope.end);
+ }
+ })
+ .error(function (err) {
+ Falcon.logResponse('error', err, type);
+
+ });
+ };
+
+ var suspendInstance = function (type, name, start, end, refresh) {
+ Falcon.logRequest();
+ Falcon.postSuspendInstance(type, name, start, end)
+ .success(function (message) {
+ Falcon.logResponse('success', message, type);
+ if(refresh){
+ scope.$parent.refreshInstanceList(scope.type, scope.name, scope.start, scope.end);
+ }
+ })
+ .error(function (err) {
+ Falcon.logResponse('error', err, type);
+
+ });
+ };
+
+ var reRunInstance = function (type, name, start, end, refresh) {
+ Falcon.logRequest();
+ Falcon.postReRunInstance(type, name, start, end)
+ .success(function (message) {
+ Falcon.logResponse('success', message, type);
+ if(refresh){
+ scope.$parent.refreshInstanceList(scope.type, scope.name, scope.start, scope.end);
+ }
+ })
+ .error(function (err) {
+ Falcon.logResponse('error', err, type);
+
+ });
+ };
+
+ var killInstance = function (type, name, start, end, refresh) {
+ Falcon.logRequest();
+ Falcon.postKillInstance(type, name, start, end)
+ .success(function (message) {
+ Falcon.logResponse('success', message, type);
+ if(refresh){
+ scope.$parent.refreshInstanceList(scope.type, scope.name, scope.start, scope.end);
+ }
+ })
+ .error(function (err) {
+ Falcon.logResponse('error', err, type);
+
+ });
+ };
+
+ scope.scopeResume = function () {
+ for(var i = 0; i < scope.selectedRows.length; i++) {
+ var multiRequestType = scope.selectedRows[i].type.toLowerCase();
+ Falcon.responses.multiRequest[multiRequestType] += 1;
+ var start = scope.selectedRows[i].instance;
+ var end = addOneMin(start);
+ var refresh = i === scope.selectedRows.length-1 ? true : false;
+ resumeInstance(scope.type, scope.name, start, end, refresh);
+ }
+ };
+
+ scope.scopeSuspend = function () {
+ for(var i = 0; i < scope.selectedRows.length; i++) {
+ var multiRequestType = scope.selectedRows[i].type.toLowerCase();
+ Falcon.responses.multiRequest[multiRequestType] += 1;
+ var start = scope.selectedRows[i].instance;
+ var end = addOneMin(start);
+ var refresh = i === scope.selectedRows.length-1 ? true : false;
+ suspendInstance(scope.type, scope.name, start, end, refresh);
+ }
+ };
+
+ scope.scopeRerun = function () {
+ for(var i = 0; i < scope.selectedRows.length; i++) {
+ var multiRequestType = scope.selectedRows[i].type.toLowerCase();
+ Falcon.responses.multiRequest[multiRequestType] += 1;
+ var start = scope.selectedRows[i].instance;
+ var end = addOneMin(start);
+ var refresh = i === scope.selectedRows.length-1 ? true : false;
+ reRunInstance(scope.type, scope.name, start, end, refresh);
+ }
+ };
+
+ scope.scopeKill = function () {
+ for(var i = 0; i < scope.selectedRows.length; i++) {
+ var multiRequestType = scope.selectedRows[i].type.toLowerCase();
+ Falcon.responses.multiRequest[multiRequestType] += 1;
+ var start = scope.selectedRows[i].instance;
+ var end = addOneMin(start);
+ var refresh = i === scope.selectedRows.length-1 ? true : false;
+ killInstance(scope.type, scope.name, start, end, refresh);
+ }
+ };
+
+ scope.download = function() {
+ var i;
+ for(i = 0; i < scope.selectedRows.length; i++) {
+ scope.downloadEntity(scope.selectedRows[i].logFile);
+ }
+ };
+
+ scope.scopeGoPage = function (page) {
+ scope.goPage(page);
+ };
+
+ scope.scopeNextOffset = function (page) {
+ var offset = (parseInt(scope.pages[0].label)+(visiblePages-1))*resultsPerPage;
+ scope.changePagesSet(offset, page, 0, scope.start, scope.end);
+ };
+
+ scope.scopePrevOffset = function (page) {
+ var offset = (parseInt(scope.pages[0].label)-(visiblePages+1))*resultsPerPage;
+ scope.changePagesSet(offset, page, visiblePages-1, scope.start, scope.end);
+ };
+
+ scope.validateDate = function(event, type){
+ var which = event.which || event.keyCode;
+ var charStr = String.fromCharCode(which);
+ event.preventDefault();
+ if (
+ which == 8 || which == 46 || which == 37 || which == 39 ||
+ (which >= 48 && which <= 57)
+ ) {
+ if(type == "start"){
+ if(scope.startFilter){
+ if(scope.startFilter.length == 1){
+ //mm
+ var prevChar = scope.startFilter.substring(scope.startFilter.length-1);
+ prevChar = parseInt(prevChar);
+ if(prevChar < 1){
+ if(prevChar == 0 && charStr == 0){
+
+ }else if(charStr <= 9){
+ scope.startFilter += charStr + "/";
+ }
+ }else{
+ if(charStr <= 2){
+ scope.startFilter += charStr + "/";
+ }
+ }
+ }else if(scope.startFilter.length == 2){
+ //mm/
+ if(charStr <= 3){
+ scope.startFilter += "/" + charStr;
+ }
+ }else if(scope.startFilter.length == 3){
+ //mm/d
+ if(charStr <= 3){
+ scope.startFilter += charStr;
+ }
+ }else if(scope.startFilter.length == 4){
+ //mm/dd
+ var prevChar = scope.startFilter.substring(scope.startFilter.length-1);
+ prevChar = parseInt(prevChar);
+ if(prevChar < 3){
+ if(prevChar == 0 && charStr == 0){
+
+ }else if(charStr <= 9){
+ scope.startFilter += charStr + "/";
+ }
+ }else{
+ if(charStr <= 1){
+ scope.startFilter += charStr + "/";
+ }
+ }
+ }else if(scope.startFilter.length == 5){
+ //mm/dd/
+ if(charStr <= 2){
+ scope.startFilter += "/" + charStr;
+ }
+ }else if(scope.startFilter.length == 6){
+ //mm/dd/y
+ if(charStr <= 2){
+ scope.startFilter += charStr;
+ }
+ }else if(scope.startFilter.length == 7){
+ //mm/dd/yy
+ if(charStr <= 9){
+ scope.startFilter += charStr;
+ }
+ }else if(scope.startFilter.length == 8){
+ //mm/dd/yyy
+ if(charStr <= 9){
+ scope.startFilter += charStr;
+ }
+ }else if(scope.startFilter.length == 9){
+ //mm/dd/yyyy
+ if(charStr <= 9){
+ scope.startFilter += charStr + " ";
+ }
+ }else if(scope.startFilter.length == 10){
+ //mm/dd/yyyy
+ if(charStr <= 2){
+ scope.startFilter += " " + charStr;
+ }
+ }else if(scope.startFilter.length == 11){
+ //mm/dd/yyyy h
+ if(charStr <= 2){
+ scope.startFilter += charStr;
+ }
+ }else if(scope.startFilter.length == 12){
+ //mm/dd/yyyy hh
+ var prevChar = scope.startFilter.substring(scope.startFilter.length-1);
+ prevChar = parseInt(prevChar);
+ if(prevChar < 2){
+ if(charStr <= 9){
+ scope.startFilter += charStr + ":";
+ }
+ }else{
+ if(charStr <= 4){
+ scope.startFilter += charStr + ":";
+ }
+ }
+ }else if(scope.startFilter.length == 13){
+ //mm/dd/yyyy hh:
+ if(charStr <= 5){
+ scope.startFilter += ":" + charStr;
+ }
+ }else if(scope.startFilter.length == 14){
+ //mm/dd/yyyy hh:m
+ if(charStr <= 5){
+ scope.startFilter += charStr;
+ }
+ }else if(scope.startFilter.length == 15){
+ //mm/dd/yyyy hh:mm
+ if(charStr <= 9){
+ scope.startFilter += charStr;
+ scope.startFilterError = false;
+ }
+ }
+ }else{
+ //m
+ if(charStr <= 1){
+ scope.startFilter = charStr;
+ }
+ }
+ }else{
+ if(scope.endFilter){
+ if(scope.endFilter.length == 1){
+ //mm
+ var prevChar = scope.endFilter.substring(scope.endFilter.length-1);
+ prevChar = parseInt(prevChar);
+ if(prevChar < 1){
+ if(prevChar == 0 && charStr == 0){
+
+ }else if(charStr <= 9){
+ scope.endFilter += charStr + "/";
+ }
+ }else{
+ if(charStr <= 2){
+ scope.endFilter += charStr + "/";
+ }
+ }
+ }else if(scope.endFilter.length == 2){
+ //mm/
+ if(charStr <= 3){
+ scope.endFilter += "/" + charStr;
+ }
+ }else if(scope.endFilter.length == 3){
+ //mm/d
+ if(charStr <= 3){
+ scope.endFilter += charStr;
+ }
+ }else if(scope.endFilter.length == 4){
+ //mm/dd
+ var prevChar = scope.endFilter.substring(scope.endFilter.length-1);
+ prevChar = parseInt(prevChar);
+ if(prevChar < 3){
+ if(prevChar == 0 && charStr == 0){
+
+ }else if(charStr <= 9){
+ scope.endFilter += charStr + "/";
+ }
+ }else{
+ if(charStr <= 1){
+ scope.endFilter += charStr + "/";
+ }
+ }
+ }else if(scope.endFilter.length == 5){
+ //mm/dd/
+ if(charStr <= 2){
+ scope.endFilter += "/" + charStr;
+ }
+ }else if(scope.endFilter.length == 6){
+ //mm/dd/y
+ if(charStr <= 2){
+ scope.endFilter += charStr;
+ }
+ }else if(scope.endFilter.length == 7){
+ //mm/dd/yy
+ if(charStr <= 9){
+ scope.endFilter += charStr;
+ }
+ }else if(scope.endFilter.length == 8){
+ //mm/dd/yyy
+ if(charStr <= 9){
+ scope.endFilter += charStr;
+ }
+ }else if(scope.endFilter.length == 9){
+ //mm/dd/yyyy
+ if(charStr <= 9){
+ scope.endFilter += charStr + " ";
+ }
+ }else if(scope.endFilter.length == 10){
+ //mm/dd/yyyy
+ if(charStr <= 2){
+ scope.endFilter += " " + charStr;
+ }
+ }else if(scope.endFilter.length == 11){
+ //mm/dd/yyyy h
+ if(charStr <= 2){
+ scope.endFilter += charStr;
+ }
+ }else if(scope.endFilter.length == 12){
+ //mm/dd/yyyy hh
+ var prevChar = scope.endFilter.substring(scope.endFilter.length-1);
+ prevChar = parseInt(prevChar);
+ if(prevChar < 2){
+ if(charStr <= 9){
+ scope.endFilter += charStr + ":";
+ }
+ }else{
+ if(charStr <= 4){
+ scope.endFilter += charStr + ":";
+ }
+ }
+ }else if(scope.endFilter.length == 13){
+ //mm/dd/yyyy hh:
+ if(charStr <= 5){
+ scope.endFilter += ":" + charStr;
+ }
+ }else if(scope.endFilter.length == 14){
+ //mm/dd/yyyy hh:m
+ if(charStr <= 5){
+ scope.endFilter += charStr;
+ }
+ }else if(scope.endFilter.length == 15){
+ //mm/dd/yyyy hh:mm
+ if(charStr <= 9){
+ scope.endFilter += charStr;
+ scope.endFilterError = false;
+ }
+ }
+ }else{
+ //m
+ if(charStr <= 1){
+ scope.endFilter = charStr;
+ }
+ }
+ }
+ }
+ };
+
+ var changeDateFormat = function(date){
+ var completeDate = date.split(" ");
+ var dates = completeDate[0].split("/");
+ date = dates[2] + "-" + dates[0] + "-" + dates[1] + "T" + completeDate[1] + "Z";
+ return date;
+ };
+
+ var validateDateFormat = function(date){
+ var char = date.substring(0, 1);
+ if(isNaN(char)){
+ return false;
+ }
+ char = date.substring(1, 2);
+ if(isNaN(char)){
+ return false;
+ }
+ char = date.substring(2, 3);
+ if(char != "/"){
+ return false;
+ }
+ char = date.substring(3, 4);
+ if(isNaN(char)){
+ return false;
+ }
+ char = date.substring(4, 5);
+ if(isNaN(char)){
+ return false;
+ }
+ char = date.substring(5, 6);
+ if(char != "/"){
+ return false;
+ }
+ char = date.substring(6, 7);
+ if(isNaN(char)){
+ return false;
+ }
+ char = date.substring(7, 8);
+ if(isNaN(char)){
+ return false;
+ }
+ char = date.substring(8, 9);
+ if(isNaN(char)){
+ return false;
+ }
+ char = date.substring(9, 10);
+ if(isNaN(char)){
+ return false;
+ }
+ char = date.substring(10, 11);
+ if(char != " "){
+ return false;
+ }
+ char = date.substring(11, 12);
+ if(isNaN(char)){
+ return false;
+ }
+ char = date.substring(12, 13);
+ if(isNaN(char)){
+ return false;
+ }
+ char = date.substring(13, 14);
+ if(char != ":"){
+ return false;
+ }
+ char = date.substring(14, 15);
+ if(isNaN(char)){
+ return false;
+ }
+ char = date.substring(15, 16);
+ if(isNaN(char)){
+ return false;
+ }
+ return true;
+ };
+
+ scope.filterInstances = function(orderBy){
+ var start;
+ var end;
+ var executeFilter = false;
+ scope.startFilterError = false;
+ scope.endFilterError = false;
+ scope.startAfterEndError = false;
+ scope.startAfterNominalError = false;
+ scope.startBeforeNominalError = false;
+ scope.endAfterNominalError = false;
+ scope.endBeforeNominalError = false;
+ var nominalStartDate = new Date(scope.start);
+ var nominalEndDate = new Date(scope.end);
+ if(scope.startFilter && scope.endFilter){
+ if(scope.startFilter.length == 16 && scope.endFilter.length == 16){
+ if(!validateDateFormat(scope.startFilter)){
+ executeFilter = false;
+ scope.startFilterError = true;
+ }else if(!validateDateFormat(scope.endFilter)){
+ executeFilter = false;
+ scope.endFilterError = true;
+ }else{
+ start = changeDateFormat(scope.startFilter);
+ var filterStartDate = new Date(start);
+ end = changeDateFormat(scope.endFilter);
+ var filterEndDate = new Date(end);
+ if(filterStartDate > filterEndDate){
+ executeFilter = false;
+ scope.startAfterEndError = true;
+ }else{
+ if(filterStartDate < nominalStartDate){
+ executeFilter = false;
+ scope.startAfterNominalError = true;
+ }else if(filterStartDate > nominalEndDate){
+ executeFilter = false;
+ scope.startBeforeNominalError = true;
+ }else if(filterEndDate < nominalStartDate){
+ executeFilter = false;
+ scope.endAfterNominalError = true;
+ }else if(filterEndDate > nominalEndDate){
+ executeFilter = false;
+ scope.endBeforeNominalError = true;
+ }else{
+ executeFilter = true;
+ }
+ }
+ }
+ }else{
+ if(scope.startFilter.length != 16){
+ scope.startFilterError = true;
+ }
+ if(scope.endFilter.length != 16){
+ scope.endFilterError = true;
+ }
+ }
+ }else if(scope.startFilter){
+ scope.endFilterError = false;
+ if(scope.startFilter.length == 16){
+ if(!validateDateFormat(scope.startFilter)){
+ executeFilter = false;
+ scope.startFilterError = true;
+ }else{
+ start = changeDateFormat(scope.startFilter);
+ var filterStartDate = new Date(start);
+ if(filterStartDate < nominalStartDate){
+ executeFilter = false;
+ scope.startAfterNominalError = true;
+ }else if(filterStartDate > nominalEndDate){
+ executeFilter = false;
+ scope.startBeforeNominalError = true;
+ }else{
+ executeFilter = true;
+ }
+ }
+ }else{
+ scope.startFilterError = true;
+ }
+ }else if(scope.endFilter){
+ scope.startFilterError = false;
+ if(scope.endFilter.length == 16){
+ if(!validateDateFormat(scope.endFilter)){
+ executeFilter = false;
+ scope.endFilterError = true;
+ }else{
+ end = changeDateFormat(scope.endFilter);
+ var filterEndDate = new Date(end);
+ if(filterEndDate < nominalStartDate){
+ executeFilter = false;
+ scope.endAfterNominalError = true;
+ }else if(filterEndDate > nominalEndDate){
+ executeFilter = false;
+ scope.endBeforeNominalError = true;
+ }else{
+ executeFilter = true;
+ }
+ }
+ }else{
+ scope.endFilterError = true;
+ }
+ }else{
+ executeFilter = true;
+ }
+
+ if(executeFilter){
+ var sortOrder = "";
+ if(orderBy){
+ if(orderBy === "startTime"){
+ if(scope.startSortOrder === "desc"){
+ scope.startSortOrder = "asc";
+ }else{
+ scope.startSortOrder = "desc";
+ }
+ sortOrder = scope.startSortOrder;
+ }else if(orderBy === "endTime"){
+ if(scope.endSortOrder === "desc"){
+ scope.endSortOrder = "asc";
+ }else{
+ scope.endSortOrder = "desc";
+ }
+ sortOrder = scope.endSortOrder;
+ }else if(orderBy === "status"){
+ if(scope.statusSortOrder === "desc"){
+ scope.statusSortOrder = "asc";
+ }else{
+ scope.statusSortOrder = "desc";
+ }
+ sortOrder = scope.statusSortOrder;
+ }
+ }else{
+ orderBy = "startTime";
+ sortOrder = "desc";
+ }
+
+ if(!start){
+ start = scope.start;
+ }
+ if(!end){
+ end = scope.end;
+ }
+
+ scope.$parent.refreshInstanceList(scope.type, scope.name, start, end, scope.statusFilter, orderBy, sortOrder);
+ }
+ }
+
+ var addOneMin = function(time){
+ var newtime = parseInt(time.substring(time.length-3, time.length-1));
+ if(newtime === 59){
+ newtime = 0;
+ }else{
+ newtime++;
+ }
+ if(newtime < 10){
+ newtime = "0"+newtime;
+ }
+ return time.substring(0, time.length-3) + newtime + "Z";
+ }
+
+ }
+ };
+ }]);
+
+})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/falcon/blob/86180d93/falcon-ui/app/js/directives/lineage-graph.js
----------------------------------------------------------------------
diff --git a/falcon-ui/app/js/directives/lineage-graph.js b/falcon-ui/app/js/directives/lineage-graph.js
new file mode 100644
index 0000000..883d2a7
--- /dev/null
+++ b/falcon-ui/app/js/directives/lineage-graph.js
@@ -0,0 +1,288 @@
+/**
+ * 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 entitiesListModule = angular.module('app.directives.lineage-graph', ['app.services' ]);
+
+ entitiesListModule.controller('LineageGraphCtrl', ['$scope', 'Falcon', 'X2jsService', '$window', 'EncodeService',
+ function($scope, Falcon, X2jsService, $window, encodeService) {
+ }]);
+
+ entitiesListModule.directive('lineageGraph', ["$timeout", 'Falcon', '$filter', function($timeout, Falcon, $filter) {
+ return {
+ scope: {
+ type: "=",
+ name: "=",
+ instance: "=",
+ start: "=",
+ end: "="
+ },
+ controller: 'LineageGraphCtrl',
+ restrict: "EA",
+ templateUrl: 'html/directives/lineageGraphDv.html',
+ link: function (scope, element) {
+
+ var CIRCLE_RADIUS = 12, RANK_SEPARATION = 120, LABEL_WIDTH = 120, LABEL_HEIGHT = 80, LABEL_PADDING = 20;
+ var PREFIX = '/api/graphs/lineage';
+
+ var data = {
+ queue : {},
+ nodes : {},
+ edges : {},
+ active_node_id : null
+ };
+
+ function process_queue(done_cb) {
+ var q = data.queue;
+ if (q.length === 0) {
+ done_cb();
+ return;
+ }
+
+ function filter_node(n) {
+ return n.type === 'process-instance' || n.type === 'feed-instance';
+ }
+
+ function is_nonterminal_label(e) {
+ return e._label === 'input' || e._label === 'output';
+ }
+
+ function visit_neighbor(p) {
+ var vid = p.id, depth = p.depth;
+ if (depth === 0) {
+ process_queue(done_cb);
+ return;
+ }
+
+ Falcon.logRequest();
+ Falcon.getInstanceVerticesDirection(vid, 'both')
+ .success(function (resp) {
+ Falcon.logResponse('success', resp, false, true);
+ for (var i = 0; i < resp.results.length; ++i) {
+ var n = resp.results[i];
+ if (data.nodes[n._id] !== undefined || !filter_node(n)) {
+ continue;
+ }
+ data.nodes[n._id] = n;
+ q.push({'id': n._id, 'depth': depth - 1});
+ }
+ })
+ .error(function (err) {
+ Falcon.logResponse('error', err, false, true);
+ })
+ .finally(function () {
+ process_queue(done_cb);
+ });
+ }
+
+ var v = data.queue.pop();
+
+ Falcon.logRequest();
+ Falcon.getInstanceVerticesDirection(v.id, 'bothE')
+ .success(function (resp) {
+ Falcon.logResponse('success', resp, false, true);
+ var terminal_has_in_edge = false, terminal_has_out_edge = false;
+ var node = data.nodes[v.id];
+ for (var i = 0; i < resp.results.length; ++i) {
+ var e = resp.results[i];
+ if (is_nonterminal_label(e)) {
+ terminal_has_in_edge = terminal_has_in_edge || e._inV === v.id;
+ terminal_has_out_edge = terminal_has_out_edge || e._outV === v.id;
+ }
+ if (data.edges[e._id] !== undefined) {
+ continue;
+ }
+ data.edges[e._id] = e;
+ }
+ node.is_terminal = !(terminal_has_in_edge && terminal_has_out_edge);
+ })
+ .error(function (err) {
+ Falcon.logResponse('error', err, false, true);
+ })
+ .finally(function () {
+ visit_neighbor(v);
+ });
+ }
+
+ function draw_graph() {
+ var svg = d3.select('#lineage-graph').html('').append('svg:g');
+
+ var LINE_FUNCTION = d3.svg.line()
+ .x(function(d) { return d.x; })
+ .y(function(d) { return d.y; })
+ .interpolate('basis');
+
+ function draw_node_functor(node_map) {
+ function expand_node(d) {
+ return function() {
+ data.queue.push({'id': d._id, 'depth': 1});
+ update_graph();
+ };
+ }
+
+ function show_info_functor(d) {
+ return function() {
+ svg.selectAll('.lineage-node').classed('lineage-node-active', false);
+ d3.select(this).classed('lineage-node-active', true);
+ data.active_node_id = d._id;
+ Falcon.logRequest();
+ Falcon.getInstanceVerticesProps(d._id)
+ .success(function (resp) {
+ Falcon.logResponse('success', resp, false, true);
+ $('#lineage-info-panel').html(resp);
+ })
+ .error(function (err) {
+ Falcon.logResponse('error', err, false, true);
+ });
+ };
+ }
+
+ return function(id, d) {
+ var data = node_map[id];
+ var r = svg.append('g')
+ .attr('transform', 'translate(' + d.x + ',' + d.y + ')');
+
+ var c = r.append('circle')
+ .attr('data-node-id', id)
+ .attr('class', 'lineage-node lineage-node-' + data.type)
+ .attr('r', CIRCLE_RADIUS)
+ .on('click', show_info_functor(data));
+
+ if (!data.is_terminal) {
+ c.on('dblclick', expand_node(data));
+ } else {
+ c.classed('lineage-node-terminal', true);
+ }
+
+ var name = node_map[id].name;
+ r.append('title').text(name);
+
+ var fo = r.append('foreignObject')
+ .attr('transform', 'translate(' + (-LABEL_WIDTH / 2) + ', ' + LABEL_PADDING + ' )')
+ .attr('width', LABEL_WIDTH)
+ .attr('height', LABEL_HEIGHT);
+
+ fo.append('xhtml:div').text(name)
+ .attr('class', 'lineage-node-text');
+ };
+ }
+
+ function draw_edge(layout) {
+ return function(e, u, v, value) {
+ var r = svg.append('g').attr('class', 'lineage-link');
+ r.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 p = points.length === 0 ? source : points[points.length - 1];
+
+ var r = CIRCLE_RADIUS;
+ var sx = p.x, sy = p.y, tx = target.x, ty = target.y;
+ var l = Math.sqrt((tx - sx) * (tx - sx) + (ty - sy) * (ty - sy));
+ var dx = r / l * (tx - sx), dy = r / l * (ty - sy);
+
+ points.unshift({'x': source.x, 'y': source.y});
+ points.push({'x': target.x - dx, 'y': target.y - dy});
+ return LINE_FUNCTION(points);
+ });
+ };
+ }
+
+ var node_map = {};
+ var g = new dagre.Digraph();
+ for (var k in data.nodes) {
+ var n = data.nodes[k];
+ g.addNode(n._id, { 'width': CIRCLE_RADIUS * 2 + LABEL_WIDTH, 'height': CIRCLE_RADIUS * 2 + LABEL_HEIGHT});
+ node_map[n._id] = n;
+ }
+
+ for (var k in data.edges) {
+ var e = data.edges[k];
+ var src = node_map[e._inV], dst = node_map[e._outV];
+ if (src !== undefined && dst !== undefined) {
+ g.addEdge(null, e._outV, e._inV);
+ }
+ }
+
+ var layout = dagre.layout().rankSep(RANK_SEPARATION).rankDir('LR').run(g);
+ layout.eachEdge(draw_edge(layout));
+ layout.eachNode(draw_node_functor(node_map));
+
+ function post_render() {
+ 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: #ccc')
+ .append('svg:path')
+ .attr('d', 'M 0 0 L 10 5 L 0 10 z');
+ }
+
+ var bb = layout.graph();
+ $('#lineage-graph').attr('width', bb.width);
+ //$('#lineage-graph').attr('width', '100%');
+ $('#lineage-graph').attr('height', bb.height);
+ post_render();
+ }
+
+ function update_graph() {
+ process_queue(function() {
+ draw_graph();
+ var id = data.active_node_id;
+ if (id !== null) {
+ d3.select('.node[data-node-id="' + id + '"]').classed('node-active', true);
+ }
+ });
+ }
+
+ var loadLineageGraph = function(entityId, instance_name) {
+ var node_name = entityId + '/' + instance_name;
+ Falcon.logRequest();
+ Falcon.getInstanceVertices(node_name)
+ .success(function (resp) {
+ Falcon.logResponse('success', resp, false, true);
+ var n = resp.results[0];
+ data.queue = [{'id': n._id, 'depth': 1}];
+ data.nodes = {};
+ data.nodes[n._id] = n;
+ update_graph();
+ })
+ .error(function (err) {
+ Falcon.logResponse('error', err, false, true);
+ });
+ };
+
+ loadLineageGraph(scope.name, scope.instance);
+
+ }
+ };
+ }]);
+
+})();
\ No newline at end of file