You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@eagle.apache.org by ha...@apache.org on 2015/11/30 03:51:09 UTC
[35/44] incubator-eagle git commit: update pom using npm to install
web deps
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/2c3005c9/eagle-webservice/src/main/webapp/app/public/js/ctrl/policyController.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/ctrl/policyController.js b/eagle-webservice/src/main/webapp/app/public/js/ctrl/policyController.js
new file mode 100644
index 0000000..a9af862
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/app/public/js/ctrl/policyController.js
@@ -0,0 +1,885 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+
+// =============================================================
+// = Policy List =
+// =============================================================
+damControllers.controller('policyListCtrl', function(globalContent, Site, damContent, $scope, $routeParams, Entities) {
+ globalContent.setConfig(damContent.config);
+ globalContent.pageTitle = "Policy List";
+ globalContent.pageSubTitle = Site.current().name;
+ globalContent.navPath = ["Policy View", "Polict List"];
+
+ // Initial load
+ $scope.policyList = [];
+ if($routeParams.dataSource) {
+ $scope.dataSource = Site.current().dataSrcList.find($routeParams.dataSource);
+ }
+
+ // List policies
+ var _policyList = Entities.queryEntities("AlertDefinitionService", {site: Site.current().name, dataSource: $routeParams.dataSource});
+ _policyList._promise.then(function() {
+ $.each(_policyList, function(i, policy) {
+ if($.inArray(policy.tags.dataSource, app.config.dataSource.uiInvisibleList) === -1) {
+ policy.__mailStr = common.getValueByPath(common.parseJSON(policy.notificationDef, {}), "0.recipients", "");
+ policy.__mailList = policy.__mailStr.trim() === "" ? [] : policy.__mailStr.split(/[\,\;]/);
+ policy.__expression = common.parseJSON(policy.policyDef, {}).expression;
+
+ $scope.policyList.push(policy);
+ }
+ });
+ });
+ $scope.policyList._promise = _policyList._promise;
+
+ // Function
+ $scope.searchFunc = function(item) {
+ var key = $scope.search;
+ if(!key) return true;
+
+ var _key = key.toLowerCase();
+ function _hasKey(item, path) {
+ var _value = common.getValueByPath(item, path, "").toLowerCase();
+ return _value.indexOf(_key) !== -1;
+ }
+ return _hasKey(item, "tags.policyId") || _hasKey(item, "__expression") || _hasKey(item, "desc") || _hasKey(item, "owner") || _hasKey(item, "__mailStr");
+ };
+
+ $scope.updatePolicyStatus = damContent.updatePolicyStatus;
+ $scope.deletePolicy = function(policy) {
+ damContent.deletePolicy(policy, function(policy) {
+ var _index = $scope.policyList.indexOf(policy);
+ $scope.policyList.splice(_index, 1);
+ });
+ };
+});
+
+// =============================================================
+// = Policy Detail =
+// =============================================================
+damControllers.controller('policyDetailCtrl', function(globalContent, Site, damContent, charts, $scope, $routeParams, Entities) {
+ var MAX_PAGESIZE = 10000;
+
+ globalContent.setConfig(damContent.config);
+ globalContent.pageTitle = "Policy Detail";
+ globalContent.navPath = ["Policy View", "Polict List", "Polict Detail"];
+ globalContent.lockSite = true;
+
+ charts = charts($scope);
+
+ $scope.common = common;
+
+ // Query policy
+ if($routeParams.encodedRowkey) {
+ $scope.policyList = Entities.queryEntity("AlertDefinitionService", $routeParams.encodedRowkey);
+ } else {
+ $scope.policyList = Entities.queryEntities("AlertDefinitionService", {
+ policyId: $routeParams.policy,
+ site: $routeParams.site,
+ alertExecutorId: $routeParams.executor
+ });
+ }
+ $scope.policyList._promise.then(function() {
+ if($scope.policyList.length === 0) {
+ $.dialog({
+ title: "OPS!",
+ content: "Policy not found!",
+ }, function() {
+ location.href = "#/dam/policyList";
+ });
+ return;
+ } else {
+ var policy = $scope.policyList[0];
+
+ policy.__mailStr = common.getValueByPath(common.parseJSON(policy.notificationDef, {}), "0.recipients", "");
+ policy.__mailList = policy.__mailStr.trim() === "" ? [] : policy.__mailStr.split(/[\,\;]/);
+ policy.__expression = common.parseJSON(policy.policyDef, {}).expression;
+
+ $scope.policy = policy;
+ Site.current(Site.find($scope.policy.tags.site));
+ console.log($scope.policy);
+ }
+
+ // Visualization
+ var _cond = {
+ dataSource: policy.tags.dataSource,
+ policyId: policy.tags.policyId,
+ _duration: 1000 * 60 * 60 * 24 * 30,
+ };
+
+ // > eagle.policy.eval.count
+ $scope.policyEvalSeries = Entities.querySeries("GenericMetricService", $.extend({_metricName: "eagle.policy.eval.count"}, _cond), "@cluster", "sum(value)", 60 * 24);
+
+ // > eagle.policy.eval.fail.count
+ $scope.policyEvalFailSeries = Entities.querySeries("GenericMetricService", $.extend({_metricName: "eagle.policy.eval.fail.count"}, _cond), "@cluster", "sum(value)", 60 * 24);
+
+ // > eagle.alert.count
+ $scope.alertSeries = Entities.querySeries("GenericMetricService", $.extend({_metricName: "eagle.alert.count"}, _cond), "@cluster", "sum(value)", 60 * 24);
+
+ // > eagle.alert.fail.count
+ $scope.alertFailSeries = Entities.querySeries("GenericMetricService", $.extend({_metricName: "eagle.alert.fail.count"}, _cond), "@cluster", "sum(value)", 60 * 24);
+
+ // Alert list
+ $scope.alertList = Entities.queryEntities("AlertService", {
+ site: Site.current().name,
+ dataSource: policy.tags.dataSource,
+ policyId: policy.tags.policyId,
+ _pageSize: MAX_PAGESIZE,
+ _duration: 1000 * 60 * 60 * 24 * 30,
+ __ETD: 1000 * 60 * 60 * 24
+ });
+ });
+
+ // Function
+ $scope.updatePolicyStatus = damContent.updatePolicyStatus;
+ $scope.deletePolicy = function(policy) {
+ damContent.deletePolicy(policy, function(policy) {
+ location.href = "#/dam/policyList";
+ });
+ };
+});
+
+// =============================================================
+// = Policy Edit =
+// =============================================================
+(function() {
+ function policyCtrl(create, globalContent, Site, damContent, $scope, $routeParams, $location, $q, Entities) {
+ globalContent.setConfig(damContent.config);
+ globalContent.pageTitle = "Policy Edit";
+ globalContent.pageSubTitle = Site.current().name;
+ globalContent.navPath = ["Policy View", "Polict List", "Polict Edit"];
+
+ var _winTimeDesc = "Number unit[millisecond, sec, min, hour, day, month, year]. e.g. 23 sec";
+ var _winTimeRegex = /^\d+\s+(millisecond|milliseconds|second|seconds|sec|minute|minutes|min|hour|hours|day|days|week|weeks|month|months|year|years)$/;
+ var _winTimeDefaultValue = '10 min';
+ $scope.config = {
+ window: [
+ // Display name, window type, required columns[Title, Description || "LONG_FIELD", Regex check, default value]
+ {
+ title: "Message Time Slide",
+ description: "Using timestamp filed from input is used as event's timestamp",
+ type: "externalTime",
+ fields:[
+ {title: "Field", defaultValue: "timestamp", hide: true},
+ {title: "Time", description: _winTimeDesc, regex: _winTimeRegex, defaultValue: _winTimeDefaultValue},
+ ]
+ },
+ {
+ title: "System Time Slide",
+ description: "Using System time is used as timestamp for event's timestamp",
+ type: "time",
+ fields:[
+ {title: "Time", description: _winTimeDesc, regex: _winTimeRegex, defaultValue: _winTimeDefaultValue},
+ ]
+ },
+ {
+ title: "System Time Batch",
+ description: "Same as System Time Window except the policy is evaluated at fixed duration",
+ type: "timeBatch",
+ fields:[
+ {title: "Time", description: _winTimeDesc, regex: _winTimeRegex, defaultValue: _winTimeDefaultValue},
+ ]
+ },
+ {
+ title: "Length Slide",
+ description: "The slide window has a fixed length",
+ type: "length",
+ fields:[
+ {title: "Number", description: "Number only. e.g. 1023", regex: /^\d+$/},
+ ]
+ },
+ {
+ title: "Length Batch",
+ description: "Same as Length window except the policy is evaluated in batch mode when fixed event count reached",
+ type: "lengthBatch",
+ fields:[
+ {title: "Number", description: "Number only. e.g. 1023", regex: /^\d+$/},
+ ]
+ },
+ ],
+ };
+
+ $scope.create = create;
+ $scope.encodedRowkey = $routeParams.encodedRowkey;
+
+ $scope.step = 0;
+ $scope.dataSources = {};
+ $scope.policy = {};
+
+ // ==========================================
+ // = Data Preparation =
+ // ==========================================
+ // Steam list
+ var _streamList = Entities.queryEntities("AlertStreamService", '@streamName=~".*"');
+ var _executorList = Entities.queryEntities("AlertExecutorService", '@streamName=~".*"');
+ $scope.streamList = _streamList;
+ $scope.executorList = _executorList;
+ $scope.streamReady = false;
+
+ $q.all([_streamList._promise, _executorList._promise]).then(function() {
+ // Map executor with stream
+ $.each(_executorList, function(i, executor) {
+ $.each(_streamList, function(j, stream) {
+ if(stream.tags.dataSource === executor.tags.dataSource && stream.tags.streamName === executor.tags.streamName) {
+ stream.alertExecutor = executor;
+ return false;
+ }
+ });
+ });
+
+ // Fill stream list
+ $.each(_streamList, function(i, unit) {
+ var _srcStreamList = $scope.dataSources[unit.tags.dataSource] = $scope.dataSources[unit.tags.dataSource] || [];
+ _srcStreamList.push(unit);
+ });
+
+ $scope.streamReady = true;
+
+ // ==========================================
+ // = Function =
+ // ==========================================
+ function _findStream(dataSource, streamName) {
+ var _streamList = $scope.dataSources[dataSource];
+ if(!_streamList) return null;
+
+ for(var i = 0 ; i < _streamList.length ; i += 1) {
+ if(_streamList[i].tags.streamName === streamName) {
+ return _streamList[i];
+ }
+ }
+ return null;
+ };
+
+ // ==========================================
+ // = Step control =
+ // ==========================================
+ $scope.steps = [
+ // >> Select data source
+ {
+ title: "Select Data Source",
+ ready: function() {
+ return $scope.streamReady;
+ },
+ init: function() {
+ if(create) $scope.policy.tags.dataSource = $scope.policy.tags.dataSource || Site.current().dataSrcList[0].tags.dataSource;
+ },
+ nextable: function() {
+ return common.getValueByPath($scope.policy, "tags.dataSource");
+ },
+ },
+
+ // >> Select stream
+ {
+ title: "Select Stream",
+ ready: function() {
+ return $scope.streamReady;
+ },
+ init: function() {
+ $scope.policy.__.streamName = $scope.policy.__.streamName ||
+ common.array.find($scope.policy.tags.dataSource, _streamList, "tags.dataSource").tags.streamName;
+ },
+ nextable: function() {
+ var _streamName = common.getValueByPath($scope.policy, "__.streamName");
+ if(!_streamName) return false;
+
+ // Detect stream in current data source list
+ return !!common.array.find(_streamName, $scope.dataSources[$scope.policy.tags.dataSource], "tags.streamName");
+ },
+ },
+
+ // >> Define Alert Policy
+ {
+ title: "Define Alert Policy",
+ init: function() {
+ // Normal mode will fetch meta list
+ if(!$scope.policy.__.advanced) {
+ var _stream = _findStream($scope.policy.tags.dataSource, $scope.policy.__.streamName);
+ $scope._stream = _stream;
+
+ if(!_stream.metas) {
+ _stream.metas = Entities.queryEntities("AlertStreamSchemaService", {dataSource: $scope.policy.tags.dataSource, streamName: $scope.policy.__.streamName});
+ _stream.metas._promise.then(function() {
+ _stream.metas.sort(function(a, b) {
+ if(a.tags.attrName < b.tags.attrName) {
+ return -1;
+ } else if(a.tags.attrName > b.tags.attrName) {
+ return 1;
+ }
+ return 0;
+ });
+ });
+ }
+ }
+ },
+ ready: function() {
+ if(!$scope.policy.__.advanced) {
+ return $scope._stream.metas._promise.$$state.status === 1;
+ }
+ return true;
+ },
+ nextable: function() {
+ if($scope.policy.__.advanced) {
+ // Check stream source
+ $scope._stream = null;
+ $.each($scope.dataSources[$scope.policy.tags.dataSource], function(i, stream) {
+ if(($scope.policy.__._expression || "").indexOf(stream.tags.streamName) !== -1) {
+ $scope._stream = stream;
+ return false;
+ }
+ });
+ return $scope._stream;
+ } else {
+ // Window
+ if($scope.policy.__.windowConfig) {
+ var _winMatch = true;
+ var _winConds = $scope.getWindow().fields;
+ $.each(_winConds, function(i, cond) {
+ if(!(cond.val || "").match(cond.regex)) {
+ _winMatch = false;
+ return false;
+ }
+ });
+ if(!_winMatch) return false;
+
+ // Aggregation
+ if($scope.policy.__.groupAgg) {
+ if(!$scope.policy.__.groupAggPath ||
+ !$scope.policy.__.groupCondOp ||
+ !$scope.policy.__.groupCondVal) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ },
+ },
+
+ // >> Configuration & Notification
+ {
+ title: "Configuration & Notification",
+ nextable: function() {
+ return !!$scope.policy.tags.policyId;
+ },
+ }
+ ];
+
+ // ==========================================
+ // = Policy Logic =
+ // ==========================================
+ _streamList._promise.then(function() {
+ // Initial policy entity
+ if(create) {
+ $scope.policy = {
+ __: {
+ toJSON: jQuery.noop,
+ conditions: {},
+ notification: [],
+ dedupe: {
+ alertDedupIntervalMin: 10,
+ emailDedupIntervalMin: 10,
+ },
+ policy: {},
+ window: "externalTime",
+ group: "",
+ groupAgg: "count",
+ groupAggPath: "timestamp",
+ groupCondOp: ">=",
+ groupCondVal: "2",
+ },
+ desc: "",
+ enabled: true,
+ prefix: "alertdef",
+ remediationDef: "",
+ tags: {
+ policyType: "siddhiCEPEngine",
+ }
+ };
+
+ // If configured data source
+ if($routeParams.dataSrc) {
+ $scope.policy.tags.dataSource = $routeParams.dataSrc;
+ if(common.array.find($routeParams.dataSrc, Site.current().dataSrcList, "tags.dataSource")) {
+ setTimeout(function() {
+ $scope.changeStep(0, 2, false);
+ $scope.$apply();
+ }, 1);
+ }
+ }
+ } else {
+ var _policy = Entities.queryEntity("AlertDefinitionService", $scope.encodedRowkey);
+ _policy._promise.then(function() {
+ if(_policy.length) {
+ $scope.policy = _policy[0];
+ $scope.policy.__ = {
+ toJSON: jQuery.noop,
+ };
+
+ Site.current(Site.find($scope.policy.tags.site));
+ } else {
+ $.dialog({
+ title: "OPS",
+ content: "Policy not found!",
+ }, function() {
+ $location.path("/dam/policyList");
+ $scope.$apply();
+ });
+ return;
+ }
+
+ // === Revert inner data ===
+ // >> De-dupe
+ $scope.policy.__.dedupe = common.parseJSON($scope.policy.dedupeDef, {});
+
+ // >> Notification
+ $scope.policy.__.notification = common.parseJSON($scope.policy.notificationDef, []);
+
+ // >> Policy
+ var _policyUnit = $scope.policy.__.policy = common.parseJSON($scope.policy.policyDef);
+
+ // >> Parse expression
+ $scope.policy.__.conditions = {};
+ var _condition = _policyUnit.expression.match(/from\s+(\w+)(\[(.*)\])?(#window[^\)]*\))?\s+(select (\w+\, )?(\w+)\((\w+)\) as aggValue (group by (\w+) )?having aggValue ([<>=]+) ([^\s]+))?/);
+ var _cond_stream = _condition[1];
+ var _cond_query = _condition[3] || "";
+ var _cond_window = _condition[4];
+ var _cond_group = _condition[5];
+ var _cond_groupUnit = _condition.slice(7,13);
+
+ if(!_condition) {
+ $scope.policy.__.advanced = true;
+ } else {
+ // > StreamName
+ var _streamName = _cond_stream;
+ var _cond = _cond_query;
+
+ $scope.policy.__.streamName = _streamName;
+
+ // > Conditions
+ // Loop condition groups
+ if(_cond.trim() !== "" && /^\(.*\)$/.test(_cond)) {
+ var _condGrps = _cond.substring(1, _cond.length - 1).split(/\)\s+and\s+\(/);
+ $.each(_condGrps, function(i, line) {
+ // Loop condition cells
+ var _condCells = line.split(/\s+or\s+/);
+ $.each(_condCells, function(i, cell) {
+ var _opMatch = cell.match(/(\S*)\s*(==|!=|>|<|>=|<=|contains)\s*('(?:[^'\\]|\\.)*'|[\w\d]+)/);
+ if(!common.isEmpty(_opMatch)) {
+ var _key = _opMatch[1];
+ var _op = _opMatch[2];
+ var _val = _opMatch[3];
+ var _conds = $scope.policy.__.conditions[_key] = $scope.policy.__.conditions[_key] || [];
+ var _type = "";
+ if(_val.match(/\'.*\'/)) {
+ _val = _val.slice(1, -1);
+ _type = "string";
+ } else if(_val === "true" || _val === "false") {
+ var _regexMatch = _key.match(/^str:regexp\((\w+),'(.*)'\)/);
+ var _containsMatch = _key.match(/^str:contains\((\w+),'(.*)'\)/);
+ var _mathes = _regexMatch || _containsMatch;
+ if(_mathes) {
+ _key = _mathes[1];
+ _val = _mathes[2];
+ _type = "string";
+ _op = _regexMatch ? "regex" : "contains";
+ _conds = $scope.policy.__.conditions[_key] = $scope.policy.__.conditions[_key] || [];
+ } else {
+ _type = "bool";
+ }
+ } else {
+ _type = "number";
+ }
+ _conds.push($scope._CondUnit(_key, _op, _val, _type));
+ }
+ });
+ });
+ } else if(_cond_query !== "") {
+ $scope.policy.__.advanced = true;
+ }
+ }
+
+ if($scope.policy.__.advanced) {
+ $scope.policy.__._expression = _policyUnit.expression;
+ } else {
+ // > window
+ if(!_cond_window) {
+ $scope.policy.__.window = "externalTime";
+ $scope.policy.__.group = "";
+ $scope.policy.__.groupAgg = "count";
+ $scope.policy.__.groupAggPath = "timestamp";
+ $scope.policy.__.groupCondOp = ">=";
+ $scope.policy.__.groupCondVal = "2";
+ } else {
+ try {
+ $scope.policy.__.windowConfig = true;
+
+ var _winCells = _cond_window.match(/\.(\w+)\((.*)\)/);
+ $scope.policy.__.window = _winCells[1];
+ var _winConds = $scope.getWindow().fields;
+ $.each(_winCells[2].split(","), function(i, val) {
+ _winConds[i].val = val;
+ });
+
+ // Group
+ if(_cond_group) {
+ $scope.policy.__.group = _cond_groupUnit[3];
+ $scope.policy.__.groupAgg = _cond_groupUnit[0];
+ $scope.policy.__.groupAggPath = _cond_groupUnit[1];
+ $scope.policy.__.groupCondOp = _cond_groupUnit[4];
+ $scope.policy.__.groupCondVal = _cond_groupUnit[5];
+ } else {
+ $scope.policy.__.group = "";
+ $scope.policy.__.groupAgg = "count";
+ $scope.policy.__.groupAggPath = "timestamp";
+ $scope.policy.__.groupCondOp = ">=";
+ $scope.policy.__.groupCondVal = "2";
+ }
+ } catch(err) {
+ $scope.policy.__.window = "externalTime";
+ }
+ }
+ }
+
+ $scope.changeStep(0, 3, false);
+ });
+ }
+
+ // Start step
+ $scope.changeStep(0, 1, false);
+
+ console.log($scope.policy, $scope);
+ });
+
+ // ==========================================
+ // = Function =
+ // ==========================================
+ // UI: Highlight select step
+ $scope.stepSelect = function(step) {
+ return step === $scope.step ? "active" : "";
+ };
+
+ // UI: Collapse all
+ $scope.collapse = function(cntr) {
+ var _list = $(cntr).find(".collapse").css("height", "auto");
+ if(_list.hasClass("in")) {
+ _list.removeClass("in");
+ } else {
+ _list.addClass("in");
+ }
+ };
+
+ // Step process. Will fetch target step attribute and return boolean
+ function _check(key, step) {
+ var _step = $scope.steps[step - 1];
+ if(!_step) return;
+
+ var _value = _step[key];
+ if(typeof _value === "function") {
+ return _value();
+ } else if(typeof _value === "boolean") {
+ return _value;
+ }
+ return true;
+ }
+ // Check step is ready. Otherwise will display load animation
+ $scope.stepReady = function(step) {
+ return _check("ready", step);
+ };
+ // Check whether process next step. Otherwise will disable next button
+ $scope.checkNextable = function(step) {
+ return !_check("nextable", step);
+ };
+ // Switch step
+ $scope.changeStep = function(step, targetStep, check) {
+ if(check === false || _check("checkStep", step)) {
+ $scope.step = targetStep;
+
+ _check("init", targetStep);
+ }
+ };
+
+ // Window
+ $scope.getWindow = function() {
+ if(!$scope.policy || !$scope.policy.__) return null;
+ var _window = common.array.find($scope.policy.__.window, $scope.config.window, "type");
+ return _window;
+ };
+
+ // Aggregation
+ $scope.groupAggPathList = function() {
+ return $.grep(common.getValueByPath($scope, "_stream.metas", []), function(meta) {
+ return $.inArray(meta.attrType, ['long','integer','number']) !== -1;
+ });
+ };
+
+ $scope.updateGroupAgg = function() {
+ $scope.policy.__.groupAggPath = $scope.policy.__.groupAggPath || common.getValueByPath($scope.groupAggPathList()[0], "tags.attrName");
+
+ if($scope.policy.__.groupAgg === 'count') {
+ $scope.policy.__.groupAggPath = 'timestamp';
+ }
+ };
+
+ // Resolver
+ $scope.resolverTypeahead = function(value, resolver) {
+ var _resolverList = Entities.query("stream/attributeresolve", {
+ site: Site.current().name,
+ resolver: resolver,
+ query: value
+ });
+ return _resolverList._promise.then(function() {
+ return _resolverList;
+ });
+ };
+
+ // Used for input box when pressing enter
+ $scope.conditionPress = function(event, key, operation, value, type) {
+ if(event.which == 13) {
+ setTimeout(function() {
+ $(event.currentTarget).closest(".input-group").find("button").click();
+ }, 1);
+ }
+ };
+ // Check whether has condition
+ $scope.hasCondition = function(key, type) {
+ var _list = common.getValueByPath($scope.policy.__.conditions, key, []);
+ if(_list.length === 0) return false;
+
+ if(type === "bool") {
+ return !_list[0].ignored();
+ }
+ return true;
+ };
+ // Condition unit definition
+ $scope._CondUnit = function(key, op, value, type) {
+ var _obj = {
+ key: key,
+ op: op,
+ val: value,
+ type: type,
+ ignored: function() {
+ if(this.type === "bool" && this.val === "none") {
+ return true;
+ }
+ return false;
+ },
+ getVal: function() {
+ return this.type === "string" ? "'" + this.val + "'" : this.val;
+ },
+ toString: function() {
+ return this.op + " " + this.getVal();
+ },
+ toCondString: function() {
+ var _op = this.op === "=" ? "==" : this.op;
+ if(_op === "regex") {
+ return "str:regexp(" + this.key + "," + this.getVal() + ")==true";
+ } else if(_op === "contains") {
+ return "str:contains(" + this.key + "," + this.getVal() + ")==true";
+ } else {
+ return this.key + " " + _op + " " + this.getVal();
+ }
+ },
+ };
+ return _obj;
+ };
+ // Add condition for policy
+ $scope.addCondition = function(key, op, value, type) {
+ if(value === "" || value === undefined) return false;
+
+ var _condList = $scope.policy.__.conditions[key] = $scope.policy.__.conditions[key] || [];
+ _condList.push($scope._CondUnit(key, op, value, type));
+ return true;
+ };
+ // Convert condition list to description string
+ $scope.parseConditionDesc = function(key) {
+ return $.map($scope.policy.__.conditions[key] || [], function(cond) {
+ if(!cond.ignored()) return "[" + cond.toString() + "]";
+ }).join(" or ");
+ };
+
+ // To query
+ $scope.toQuery = function() {
+ if(!$scope.policy.__) return "";
+
+ if($scope.policy.__.advanced) return $scope.policy.__._expression;
+
+ // > Query
+ var _query = $.map(common.getValueByPath($scope.policy, "__.conditions", {}), function(list, key) {
+ var _conds = $.map(list, function(cond) {
+ if(!cond.ignored()) return cond.toCondString();
+ });
+ if(_conds.length) {
+ return "(" + _conds.join(" or ") + ")";
+ }
+ }).join(" and ");
+ if(_query) {
+ _query = "[" + _query + "]";
+ }
+
+ // > Window
+ var _window = $scope.getWindow();
+ var _windowStr = "";
+ if($scope.policy.__.windowConfig) {
+ _windowStr = $.map(_window.fields, function(field) {
+ return field.val;
+ }).join(",");
+ _windowStr = "#window." + _window.type + "(" + _windowStr + ")";
+
+ // > Group
+ if($scope.policy.__.group) {
+ _windowStr += common.template(" select ${group}, ${groupAgg}(${groupAggPath}) as aggValue group by ${group} having aggValue ${groupCondOp} ${groupCondVal}", {
+ group: $scope.policy.__.group,
+ groupAgg: $scope.policy.__.groupAgg,
+ groupAggPath: $scope.policy.__.groupAggPath,
+ groupCondOp: $scope.policy.__.groupCondOp,
+ groupCondVal: $scope.policy.__.groupCondVal,
+ });
+ } else {
+ _windowStr += common.template(" select ${groupAgg}(${groupAggPath}) as aggValue having aggValue ${groupCondOp} ${groupCondVal}", {
+ groupAgg: $scope.policy.__.groupAgg,
+ groupAggPath: $scope.policy.__.groupAggPath,
+ groupCondOp: $scope.policy.__.groupCondOp,
+ groupCondVal: $scope.policy.__.groupCondVal,
+ });
+ }
+ } else {
+ _windowStr = " select *";
+ }
+
+ return common.template("from ${stream}${query}${window} insert into outputStream;", {
+ stream: $scope.policy.__.streamName,
+ query: _query,
+ window: _windowStr,
+ });
+ };
+
+ // ==========================================
+ // = Update Policy =
+ // ==========================================
+ // dedupeDef notificationDef policyDef
+ $scope.finishPolicy = function() {
+ $scope.lock = true;
+
+ // dedupeDef
+ $scope.policy.dedupeDef = JSON.stringify($scope.policy.__.dedupe);
+
+ // notificationDef
+ $scope.policy.__.notification = $scope.policy.__.notification || [];
+ var _notificationUnit = $scope.policy.__.notification[0];
+ if(_notificationUnit) {
+ _notificationUnit.flavor = "email";
+ _notificationUnit.id = "email_1";
+ _notificationUnit.tplFileName = "";
+ }
+ $scope.policy.notificationDef = JSON.stringify($scope.policy.__.notification);
+
+ // policyDef
+ $scope.policy.__._dedupTags = $scope.policy.__._dedupTags || {};
+ $scope.policy.__.policy = {
+ expression: $scope.toQuery(),
+ type: "siddhiCEPEngine"
+ };
+ $scope.policy.policyDef = JSON.stringify($scope.policy.__.policy);
+
+ // alertExecutorId
+ if($scope._stream.alertExecutor) {
+ $scope.policy.tags.alertExecutorId = $scope._stream.alertExecutor.tags.alertExecutorId;
+ } else {
+ $scope.lock = false;
+ $.dialog({
+ title: "OPS!",
+ content: "Alert Executor not defined! Please check 'AlertExecutorService'!"
+ });
+ return;
+ }
+
+ // site
+ $scope.policy.tags.site = $scope.policy.tags.site || Site.current().name;
+
+ // Update function
+ function _updatePolicy() {
+ Entities.updateEntity("AlertDefinitionService", $scope.policy)._promise.success(function(data) {
+ $scope.create = create = false;
+ $scope.encodedRowkey = data.obj[0];
+
+ $.dialog({
+ title: "Success",
+ content: (create ? "Create" : "Update") + " success!",
+ }, function() {
+ if(data.success) {
+ Site.url(Site.find($scope.policy.tags.site), "/dam/policyList");
+ } else {
+ $.dialog({
+ title: "OPS",
+ content: (create ? "Create" : "Update") + "failed!" + JSON.stringify(data),
+ });
+ }
+ });
+ }).error(function(data) {
+ $.dialog({
+ title: "OPS",
+ content: (create ? "Create" : "Update") + "failed!" + JSON.stringify(data),
+ });
+ }).then(function() {
+ $scope.lock = false;
+ });
+ }
+
+ // Check if already exist
+ if($scope.create) {
+ var _checkList = Entities.queryEntities("AlertDefinitionService", {
+ alertExecutorId: $scope.policy.tags.alertExecutorId,
+ policyId: $scope.policy.tags.policyId,
+ policyType: "siddhiCEPEngine",
+ dataSource: $scope.policy.tags.dataSource,
+ });
+ _checkList._promise.then(function() {
+ if(_checkList.length) {
+ $.dialog({
+ title: "Override Confirm",
+ content: "Already exists PolicyID '" + $scope.policy.tags.policyId + "'. Do you want to override?",
+ confirm: true
+ }, function(ret) {
+ if(ret) {
+ _updatePolicy();
+ } else {
+ $scope.lock = false;
+ $scope.$apply();
+ }
+ });
+ } else {
+ _updatePolicy();
+ }
+ });
+ } else {
+ _updatePolicy();
+ }
+ };
+ });
+ }
+
+ damControllers.controller('policyCreateCtrl', function(globalContent, Site, damContent, $scope, $routeParams, $location, $q, Entities) {
+ policyCtrl(true, globalContent, Site, damContent, $scope, $routeParams, $location, $q, Entities);
+ });
+ damControllers.controller('policyEditCtrl', function(globalContent, Site, damContent, $scope, $routeParams, $location, $q, Entities) {
+ globalContent.lockSite = true;
+ policyCtrl(false, globalContent, Site, damContent, $scope, $routeParams, $location, $q, Entities);
+ });
+})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/2c3005c9/eagle-webservice/src/main/webapp/app/public/js/ctrl/sensitivityController.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/ctrl/sensitivityController.js b/eagle-webservice/src/main/webapp/app/public/js/ctrl/sensitivityController.js
new file mode 100644
index 0000000..0dd5ea5
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/app/public/js/ctrl/sensitivityController.js
@@ -0,0 +1,389 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+// =============================================================
+// = Sensitivity Summary =
+// =============================================================
+damControllers.controller('sensitivitySummaryCtrl', function(globalContent, Site, damContent, $scope, Entities) {
+ globalContent.setConfig(damContent.config);
+ globalContent.pageTitle = "Data Classification";
+ globalContent.pageSubTitle = Site.current().name;
+
+ $scope._sensitivitySource = null;
+ $scope._sensitivityLock = false;
+ $scope._sensitivityImportType = "By Text";
+ $scope._sensitivityFile = "";
+ $scope._sensitivityData = "";
+ $scope._sensitivityError = "";
+ $scope.sensitivityList = [];
+
+ // TODO: Only support Hive & HDFS
+ if(common.array.find("hdfsAuditLog", Site.current().dataSrcList, "tags.dataSource")) {
+ $scope.sensitivityList.push(
+ {name: "HDFS", service: "FileSensitivityService", keys: ["filedir", "sensitivityType"]}
+ );
+ }
+ if(common.array.find("hiveQueryLog", Site.current().dataSrcList, "tags.dataSource")) {
+ $scope.sensitivityList.push(
+ {name: "Hive", service: "HiveResourceSensitivityService", keys: ["hiveResource", "sensitivityType"]}
+ );
+ }
+
+ function _refreshStatistic(entity) {
+ if(entity) {
+ entity.statisitc = Entities.queryGroup(entity.service, {site: Site.current().name}, "@site", "count");
+ } else {
+ $.each($scope.sensitivityList, function(i, entity) {
+ entity.statisitc = Entities.queryGroup(entity.service, {site: Site.current().name}, "@site", "count");
+ });
+ }
+ }
+ _refreshStatistic();
+
+ // Import sensitivity data
+ $scope.showImportEditor = function(entity) {
+ $scope._sensitivitySource = entity;
+
+ $("#sensitivityMDL").modal('show');
+ setTimeout(function() {
+ $("#sensitivityData").focus();
+ }, 500);
+ };
+
+ $scope.confirmImport = function() {
+ if(!$scope._sensitivitySource) return;
+
+ $scope._sensitivityLock = true;
+
+ // Post data
+ switch($scope._sensitivityImportType) {
+ case "By Text":
+ // > By Text
+ Entities.updateEntity($scope._sensitivitySource.service,
+ common.parseJSON($scope._sensitivityData, null),
+ {timestamp: false})._promise.success(function(data) {
+ if(!Entities.dialog(data)) {
+ $("#sensitivityMDL").modal('hide');
+ $scope._sensitivityData = "";
+
+ _refreshStatistic($scope._sensitivitySource);
+ }
+ }).finally(function() {
+ $scope._sensitivityLock = false;
+ });
+ break;
+
+ case "By File":
+ // > By File
+ var formData = new FormData();
+ formData.append("file", $("#sensitivityFile")[0].files[0]);
+ formData.append("site", Site.current().name);
+
+ $.ajax({
+ url : app.getURL("updateEntity", {serviceName: $scope._sensitivitySource.service}),
+ data : formData,
+ cache : false,
+ contentType : false,
+ processData : false,
+ type : 'POST',
+ }).done(function(data) {
+ if(!Entities.dialog(data)) {
+ $("#sensitivityMDL").modal('hide');
+ $scope._sensitivityFile = "";
+
+ _refreshStatistic($scope._sensitivitySource);
+ }
+ }).always(function() {
+ $scope._sensitivityLock = false;
+ $scope.$apply();
+ });
+ }
+ };
+
+ $scope.importCheck = function() {
+ if($scope._sensitivityLock) return false;
+ $scope._sensitivityError = "";
+
+ switch($scope._sensitivityImportType) {
+ case "By Text":
+ if(!$scope._sensitivityData) return false;
+
+ var _list = common.parseJSON($scope._sensitivityData, null);
+ if(!_list) {
+ $scope._sensitivityError = "Can't parse json";
+ } else if(!$.isArray(_list)) {
+ $scope._sensitivityError = "Must be array";
+ } else if(_list.length === 0) {
+ $scope._sensitivityError = "Please provide at least one sensitivity item";
+ }
+ break;
+ case "By File":
+ if(!$scope._sensitivityFile) return false;
+ break;
+ }
+ return !$scope._sensitivityError;
+ };
+
+ // Manage sensitivity data
+ $scope.showManagementEditor = function(entity) {
+ $scope._sensitivitySource = entity;
+ $("#sensitivityListMDL").modal('show');
+
+ entity.list = Entities.queryEntities(entity.service, {site: Site.current().name});
+ };
+
+ $scope.deleteItem = function(item) {
+ $.dialog({
+ title: "Delete Confirm",
+ content: "Do you want to delete '" + item.tags[$scope._sensitivitySource.keys[0]] + "'?",
+ buttons: [
+ {name: "Delete", class: "btn btn-danger", value: true},
+ {name: "Cancel", class: "btn btn-default", value: false},
+ ]
+ }, function(ret) {
+ if(!ret) return;
+
+ common.array.remove(item, $scope._sensitivitySource.list);
+ Entities.deleteEntity($scope._sensitivitySource.service, item)._promise.then(function() {
+ _refreshStatistic($scope._sensitivitySource);
+ });
+
+
+ $scope.$apply();
+ });
+ };
+
+ $scope.deleteAll = function(entity) {
+ $.dialog({
+ title: "Delete Confirm",
+ content: "<span class='text-red fa fa-exclamation-triangle pull-left' style='font-size: 50px;'></span>" +
+ "<p>You are <strong class='text-red'>DELETING</strong> all the sensitivity data from '" + entity.name + "'!</p>" +
+ "<p>Proceed to delete?</p>",
+ buttons: [
+ {name: "Delete", class: "btn btn-danger", value: true},
+ {name: "Cancel", class: "btn btn-default", value: false},
+ ]
+ }, function(ret) {
+ if(!ret) return;
+
+ Entities.deleteEntities(entity.service, {
+ site: Site.current().name
+ })._promise.then(function() {
+ _refreshStatistic(entity);
+ });
+
+ entity.list.splice(0);
+ $scope.$apply();
+ });
+ };
+});
+
+// =============================================================
+// = Sensitivity =
+// =============================================================
+damControllers.controller('sensitivityCtrl', function(globalContent, Site, damContent, $scope, $q, $routeParams, $location, Entities) {
+ globalContent.setConfig(damContent.config);
+ globalContent.pageTitle = "Data Classification";
+ globalContent.pageSubTitle = Site.current().name;
+
+ $scope.sensitivityList = [];
+ $scope.type = $routeParams.type;
+ $scope.ajaxId = Math.random();
+
+ // TODO: Only support Hive & HDFS
+ if(common.array.find("hdfsAuditLog", Site.current().dataSrcList, "tags.dataSource")) {
+ $scope.sensitivityList.push(
+ {name: "HDFS"}
+ );
+ }
+ if(common.array.find("hiveQueryLog", Site.current().dataSrcList, "tags.dataSource")) {
+ $scope.sensitivityList.push(
+ {name: "Hive"}
+ );
+ }
+
+ // Update search
+ $scope.$watch("type", function(value) {
+ $location.search("type", value);
+ });
+});
+
+// =============================================================
+// = Sensitivity: HDFS =
+// =============================================================
+damControllers.controller('sensitivityHDFSCtrl', function(globalContent, Site, damContent, $scope, $location, $q, Entities) {
+ $scope.path = $location.search().path || "/";
+ $scope.pathUnitList = [];
+
+ $scope.items = [];
+
+ /*$scope.$parent.$on("tab-change", function(event, pane) {
+ $location.search("path", pane.title === "HDFS" ? $scope.path : null);
+ });*/
+
+ // Mark sensitivity
+ $scope._oriItem = {};
+ $scope._markItem = {};
+
+ $scope.markSensitivity = function(item) {
+ $scope._oriItem = item;
+ $scope._markItem = {
+ prefix: "fileSensitivity",
+ tags: {
+ site: Site.current().name,
+ filedir: item.resource
+ },
+ sensitivityType: ""
+ };
+ $("#sensitivityHdfsMDL").modal();
+ setTimeout(function() {
+ $("#hdfsSensitiveType").focus();
+ }, 500);
+ };
+ $scope.confirmUpateSensitivity = function() {
+ $scope._oriItem.sensitiveType = $scope._markItem.sensitivityType;
+ var _promise = Entities.updateEntity("FileSensitivityService", $scope._markItem, {timestamp: false})._promise.success(function(data) {
+ Entities.dialog(data);
+ });
+ $("#sensitivityHdfsMDL").modal('hide');
+ };
+ $scope.unmarkSensitivity = function(item) {
+ $.dialog({
+ title: "Unmark Confirm",
+ content: "Do you want to remove the sensitivity mark on '" + item.resource + "'?",
+ confirm: true
+ }, function(ret) {
+ if(!ret) return;
+
+ Entities.deleteEntities("FileSensitivityService", {
+ site: Site.current().name,
+ filedir: item.resource
+ });
+
+ item.sensitiveType = null;
+ $scope.$apply();
+ });
+ };
+
+ // Path
+ function _refreshPathUnitList(_path) {
+ var _start,_current, _unitList = [];
+ var _path = _path + (_path.match(/\/$/) ? "" : "/");
+ for(_current = _start = 0 ; _current < _path.length ; _current += 1) {
+ if(_path[_current] === "/") {
+ _unitList.push({
+ name: _path.substring(_start, _current + (_current === 0 ? 1 : 0)),
+ path: _path.substring(0, _current === 0 ? 1 : _current),
+ });
+ _start = _current + 1;
+ }
+ }
+ $scope.pathUnitList = _unitList;
+ };
+
+ // Item
+ $scope.updateItems = function(path) {
+ if(path) $scope.path = path;
+
+ $scope.items = Entities.query("hdfsResource", {site: Site.current().name, path: $scope.path});
+ $scope.items._promise.success(function(data) {
+ var $dlg = Entities.dialog(data, function() {
+ if($scope.path !== "/") $scope.updateItems("/");
+ });
+ });
+ _refreshPathUnitList($scope.path);
+
+ //$location.search("path", $scope.path);
+ };
+
+ $scope.getFileName = function(item) {
+ return (item.resource + "").replace(/^.*\//, "");
+ };
+
+ $scope.updateItems($scope.path);
+});
+
+// =============================================================
+// = Sensitivity: Hive =
+// =============================================================
+damControllers.controller('sensitivityHiveCtrl', function(globalContent, Site, damContent, $scope, $q, Entities) {
+ $scope.table = null;
+
+ $scope.databases = Entities.query("hiveResource/databases", {site: Site.current().name});
+ $scope.loadTables = function(database) {
+ if(database.tables) return;
+ database.tables = Entities.query("hiveResource/tables", {site: Site.current().name, database: database.database});
+ };
+
+ $scope.loadColumns = function(database, table) {
+ $scope.table = table;
+
+ if(table.columns) return;
+ table.columns = Entities.query("hiveResource/columns", {site: Site.current().name, database: database.database, table: table.table});
+ };
+
+ // Mark sensitivity
+ $scope._oriItem = {};
+ $scope._markItem = {};
+
+ $scope.markSensitivity = function(item, event) {
+ if(event) event.stopPropagation();
+
+ $scope._oriItem = item;
+ $scope._markItem = {
+ prefix: "hiveResourceSensitivity",
+ tags: {
+ site: Site.current().name,
+ hiveResource: item.resource
+ },
+ sensitivityType: ""
+ };
+ $("#sensitivityHiveMDL").modal();
+ setTimeout(function() {
+ $("#hiveSensitiveType").focus();
+ }, 500);
+ };
+ $scope.confirmUpateSensitivity = function() {
+ $scope._oriItem.sensitiveType = $scope._markItem.sensitivityType;
+ var _promise = Entities.updateEntity("HiveResourceSensitivityService", $scope._markItem, {timestamp: false})._promise.success(function(data) {
+ Entities.dialog(data);
+ });
+ $("#sensitivityHiveMDL").modal('hide');
+ };
+ $scope.unmarkSensitivity = function(item, event) {
+ if(event) event.stopPropagation();
+
+ $.dialog({
+ title: "Unmark Confirm",
+ content: "Do you want to remove the sensitivity mark on '" + item.resource + "'?",
+ confirm: true
+ }, function(ret) {
+ if(!ret) return;
+
+ Entities.deleteEntities("HiveResourceSensitivityService", {
+ site: Site.current().name,
+ hiveResource: item.resource
+ });
+
+ item.sensitiveType = null;
+ $scope.$apply();
+ });
+ };
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/2c3005c9/eagle-webservice/src/main/webapp/app/public/js/ctrl/siteController.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/ctrl/siteController.js b/eagle-webservice/src/main/webapp/app/public/js/ctrl/siteController.js
new file mode 100644
index 0000000..a7228bd
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/app/public/js/ctrl/siteController.js
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+// =============================================================
+// = Site List =
+// =============================================================
+damControllers.controller('siteListCtrl', function(globalContent, Site, damContent, $scope, $q, Entities) {
+ globalContent.setConfig(damContent.config);
+ globalContent.hideSite = true;
+
+ $scope._siteEntity;
+ $scope._siteEntityLock;
+
+ $scope._dataSrcEntity;
+ $scope._dataSrcEntityLock;
+
+ $scope.dataSrcList = Entities.queryGroup("AlertStreamService", '@dataSource=~".*"', "@dataSource", "count");
+
+ // Policy statistic
+ $scope.policyStatistic = Entities.queryGroup("AlertDefinitionService", '@dataSource=~".*"', "@site,@dataSource", "count");
+ $scope.getPolicyCount = function(site, dataSource) {
+ for(var i = 0 ; i < $scope.policyStatistic.length ; i += 1) {
+ var _cur = $scope.policyStatistic[i];
+ if(_cur.key[0] === site && _cur.key[1] === dataSource) {
+ return _cur.value[0];
+ }
+ }
+ return 0;
+ };
+
+ // Alert statistic
+ $scope.alertStatistic = Entities.queryGroup("AlertService", {_duration: 1000 * 60 * 60 * 24 * 30}, "@site,@dataSource", "count");
+ $scope.getAlertCount = function(site, dataSource) {
+ for(var i = 0 ; i < $scope.alertStatistic.length ; i += 1) {
+ var _cur = $scope.alertStatistic[i];
+ if(_cur.key[0] === site && _cur.key[1] === dataSource) {
+ return _cur.value[0];
+ }
+ }
+ return 0;
+ };
+
+ // =========================================== Site ===========================================
+ $scope.showSiteEditor = function(site) {
+ $("#siteMDL").modal("show");
+ setTimeout(function() {
+ $("#siteName").focus();
+ }, 500);
+
+ $scope._siteEntity = {
+ dataSrcList: {}
+ };
+ $.each($scope.dataSrcList, function(i, item) {
+ $scope._siteEntity.dataSrcList[item.key[0]] = {
+ name: item.key[0],
+ enabled: false
+ };
+ });
+
+ if(site) {
+ $scope._siteEntity.srcSite = site;
+ $scope._siteEntity.name = site.name;
+
+ $.each(site.dataSrcList, function(i, dataSrc) {
+ $scope._siteEntity.dataSrcList[dataSrc.tags.dataSource].enabled = dataSrc.enabled === undefined ? true : dataSrc.enabled;
+ });
+ }
+ };
+ $scope.checkUpdateSite = function() {
+ if(!$scope._siteEntity || !$scope._siteEntity.dataSrcList) return false;
+
+ var _hasDataSrc = !!common.array.find(true, common.map.toArray($scope._siteEntity.dataSrcList), "enabled");
+ return $scope._siteEntity.name && _hasDataSrc && !$scope._siteEntityLock;
+ };
+ $scope.confirmUpateSite = function() {
+ var promiseList = [];
+ $scope._siteEntityLock = true;
+
+ if($scope._siteEntity.srcSite) {
+ var promiseList = [];
+ $.each($scope._siteEntity.dataSrcList, function(name, dataSrc) {
+ var _entity = {
+ enabled: dataSrc.enabled,
+ tags: {
+ site: $scope._siteEntity.name,
+ dataSource: name,
+ },
+ };
+
+ if(dataSrc.enabled) {
+ promiseList.push(Entities.updateEntity("AlertDataSourceService", _entity)._promise);
+ } else {
+ var _dataSrc = common.array.find(name, $scope._siteEntity.srcSite.dataSrcList, "tags.dataSource");
+ if(_dataSrc) {
+ _dataSrc.enabled = false;
+ promiseList.push(Entities.updateEntity("AlertDataSourceService", _entity)._promise);
+ }
+ }
+ });
+ } else {
+ $.each($scope._siteEntity.dataSrcList, function(name, dataSrc) {
+ if(!dataSrc.enabled) return;
+
+ var _entity = {
+ enabled: true,
+ tags: {
+ site: $scope._siteEntity.name,
+ dataSource: name,
+ },
+ };
+ promiseList.push(Entities.updateEntity("AlertDataSourceService", _entity)._promise);
+ });
+ }
+
+ $q.all(promiseList).then(function() {
+ $("#siteMDL").modal("hide")
+ .on("hidden.bs.modal", function() {
+ $("#siteMDL").off("hidden.bs.modal");
+ Site.refresh();
+ });
+ }).finally(function() {
+ $scope._siteEntityLock = false;
+ });
+ };
+
+ $scope.deleteSite = function(site) {
+ $.dialog({
+ title: "Delete Confirm",
+ content: "<span class='text-red fa fa-exclamation-triangle pull-left' style='font-size: 50px;'></span>" +
+ "<p>You are <strong class='text-red'>DELETING</strong> the site '<strong>" + site.name + "</strong>'!</p>" +
+ "<p>Proceed to delete?</p>",
+ buttons: [
+ {name: "Delete", class: "btn btn-danger", value: true},
+ {name: "Cancel", class: "btn btn-default", value: false},
+ ]
+ }, function(ret) {
+ if(!ret) return;
+
+ Entities.deleteEntities("AlertDataSourceService", {
+ site: site.name
+ })._promise.then(function() {
+ Site.refresh();
+ });
+ });
+ };
+
+ // ======================================= Data Source ========================================
+ $scope.showDataSourceEditor = function(dataSrc) {
+ $("#dataSrcMDL").modal("show");
+ setTimeout(function() {
+ $("#dataSrcConfig").focus();
+ }, 500);
+
+ $scope._dataSrcEntity = dataSrc;
+ };
+
+ $scope.confirmUpateDataSource = function() {
+ $scope._dataSrcEntityLock = true;
+ Entities.updateEntity("AlertDataSourceService", $scope._dataSrcEntity)._promise.then(function() {
+ $("#dataSrcMDL").modal("hide");
+ }).finally(function() {
+ $scope._dataSrcEntityLock = false;
+ });
+ };
+
+ $scope.confirmDeleteDataSource = function() {
+ console.log($scope._dataSrcEntity);
+ $("#dataSrcMDL").modal("hide")
+ .on('hidden.bs.modal', function (e) {
+ $("#dataSrcMDL").off('hidden.bs.modal');
+
+ var _additionalContent = Site.find($scope._dataSrcEntity.tags.site).dataSrcList.length > 1 ? "" : "<p class='text-muted' style='margin-left: 60px;'>(This site has only one source. Delete will remove site either.)</p>";
+
+ $.dialog({
+ title: "Delete Confirm",
+ content: "<span class='text-red fa fa-exclamation-triangle pull-left' style='font-size: 50px;'></span>" +
+ "<p>You are <strong class='text-red'>DELETING</strong> the data source '<strong>" + $scope._dataSrcEntity.tags.dataSource + "</strong>' of '" + $scope._dataSrcEntity.tags.site + "'!</p>" +
+ "<p>Proceed to delete?</p>" + _additionalContent,
+ buttons: [
+ {name: "Delete", class: "btn btn-danger", value: true},
+ {name: "Cancel", class: "btn btn-default", value: false},
+ ]
+ }, function(ret) {
+ if(!ret) return;
+
+ Entities.deleteEntity("AlertDataSourceService", $scope._dataSrcEntity)._promise.then(function() {
+ Site.refresh();
+ });
+ });
+ });
+ };
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/2c3005c9/eagle-webservice/src/main/webapp/app/public/js/ctrl/streamController.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/ctrl/streamController.js b/eagle-webservice/src/main/webapp/app/public/js/ctrl/streamController.js
new file mode 100644
index 0000000..858a7a4
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/app/public/js/ctrl/streamController.js
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+// =============================================================
+// = Stream List =
+// =============================================================
+damControllers.controller('streamListCtrl', function(globalContent, damContent, $scope, $route, $routeParams, $q, Entities) {
+ globalContent.setConfig(damContent.config);
+ globalContent.hideSite = true;
+
+ $scope.streams = {};
+ $scope._streamEntity;
+ $scope._streamEntityLock = false;
+
+ // =========================================== List ===========================================
+ var _streamList = Entities.queryEntities("AlertStreamService", '@streamName=~".*"');
+ var _streamSchemaList = Entities.queryEntities("AlertStreamSchemaService", '@streamName=~".*"');
+ $scope.streamList = _streamList;
+ $scope.streamSchemaList = _streamSchemaList;
+
+ _streamList._promise.then(function() {
+ $.each(_streamList, function(i, stream) {
+ stream.metaList = [];
+ $scope.streams[stream.tags.dataSource + "_" + stream.tags.streamName] = stream;
+ });
+ });
+
+ $q.all([_streamList._promise, _streamSchemaList._promise]).then(function(res) {
+ $.each(_streamSchemaList, function(i, meta) {
+ var _stream = $scope.streams[meta.tags.dataSource + "_" + meta.tags.streamName];
+ if(_stream) {
+ _stream.metaList.push(meta);
+ } else {
+ console.warn("[Meta] Stream not match:", meta.tags.dataSource + "_" + meta.tags.streamName);
+ }
+ });
+ });
+
+ // =========================================== Edit ===========================================
+ $scope.showStreamEditor = function(stream) {
+ $("#streamMDL").modal("show");
+ setTimeout(function() {
+ $("#dataSource").focus();
+ }, 500);
+
+ $scope._streamEntity = {
+ dataSource: "",
+ streamName: "",
+ description: "",
+ metaList: [],
+ };
+ if(stream) {
+ $scope._streamEntity.srcStream = stream;
+ $scope._streamEntity.dataSource = stream.tags.dataSource;
+ $scope._streamEntity.streamName = stream.tags.streamName;
+ $scope._streamEntity.desc = stream.desc;
+
+ $scope._streamEntity.metaList = $.map(stream.metaList, function(meta) {
+ return {
+ srcMeta: meta,
+ attrName: meta.tags.attrName,
+ attrDisplayName: meta.attrDisplayName,
+ attrType: meta.attrType,
+ attrDescription: meta.attrDescription,
+ };
+ });
+ }
+ };
+
+ $scope.deleteMeta = function(meta) {
+ $.dialog({
+ title: "Delete confirm",
+ content: "<p>You are <strong class='text-red'>DELETING</strong> the meta '<strong>" + meta.attrName + "</strong>'!</p>" +
+ "<p>Proceed to delete?</p>",
+ buttons: [
+ {name: "Delete", class: "btn btn-danger", value: true},
+ {name: "Cancel", class: "btn btn-default", value: false},
+ ]
+ }, function(ret) {
+ if(!ret) return;
+
+ common.array.remove(meta, $scope._streamEntity.metaList);
+ $scope.$apply();
+ });
+ };
+
+ $scope.checkUpdateStream = function() {
+ if(!$scope._streamEntity || $scope._streamEntityLock) return false;
+
+ var _pass = true;
+
+ if(!$scope._streamEntity.dataSource) _pass = false;
+ if(!$scope._streamEntity.streamName) _pass = false;
+
+ $.each($scope._streamEntity.metaList, function(i, meta) {
+ if(!meta.attrName || !meta.attrType) {
+ _pass = false;
+ return false;
+ }
+ });
+
+ return _pass;
+ };
+
+ $scope.confirmDeleteStream = function() {
+ $.dialog({
+ title: "Delete Confirm",
+ content: "<span class='text-red fa fa-exclamation-triangle pull-left' style='font-size: 50px;'></span>" +
+ "<p>You are <strong class='text-red'>DELETING</strong> the stream '<strong>" + $scope._streamEntity.streamName + "</strong>'!</p>" +
+ "<p>Proceed to delete?</p>",
+ buttons: [
+ {name: "Delete", class: "btn btn-danger", value: true},
+ {name: "Cancel", class: "btn btn-default", value: false},
+ ]
+ }, function(ret) {
+ if(!ret) return;
+
+ var _promiseStream = Entities.deleteEntities("AlertStreamService", {
+ dataSource: $scope._streamEntity.dataSource,
+ streamName: $scope._streamEntity.streamName,
+ })._promise;
+ var _promiseStreamSchema = Entities.deleteEntities("AlertStreamSchemaService", {
+ dataSource: $scope._streamEntity.dataSource,
+ streamName: $scope._streamEntity.streamName,
+ })._promise;
+
+ $q.all(_promiseStream, _promiseStreamSchema).then(function() {
+ $("#streamMDL").modal("hide");
+
+ setTimeout(function() {
+ $route.reload();
+ }, 500);
+ });
+ });
+ };
+
+ $scope.confirmUpateStream = function() {
+ $scope._streamEntityLock = true;
+
+ // Stream entity
+ var _entity = {
+ prefix: "alertStream",
+ tags: {
+ dataSource: $scope._streamEntity.dataSource,
+ streamName: $scope._streamEntity.streamName,
+ },
+ desc: $scope._streamEntity.desc
+ };
+
+ // Merge original stream
+ if($scope._streamEntity.srcStream) {
+ $.extend(_entity, $scope._streamEntity.srcStream);
+ }
+
+ // Meta entities
+ var _metaList = $.map($scope._streamEntity.metaList, function(meta) {
+ return {
+ prefix: "alertStreamSchema",
+ attrType: meta.attrType,
+ attrDisplayName: meta.attrDisplayName,
+ attrDescription: meta.attrDescription,
+ tags: {
+ dataSource: _entity.tags.dataSource,
+ streamName: _entity.tags.streamName,
+ attrName: meta.attrName,
+ },
+ };
+ });
+
+ Entities.updateEntity("AlertStreamService", _entity)._promise.then(function() {
+ Entities.deleteEntities("AlertStreamSchemaService", {
+ dataSource: _entity.tags.dataSource,
+ streamName: _entity.tags.streamName,
+ })._promise.then(function() {
+ Entities.updateEntity("AlertStreamSchemaService", _metaList)._promise.finally(function() {
+ Site.refresh();
+ });
+ }).finally(function() {
+ $("#streamMDL").modal("hide");
+ $scope._streamEntityLock = false;
+
+ setTimeout(function() {
+ $route.reload();
+ }, 500);
+ });
+ }, function() {
+ $scope._streamEntityLock = false;
+ });
+ };
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/2c3005c9/eagle-webservice/src/main/webapp/app/public/js/ctrl/userProfileController.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/ctrl/userProfileController.js b/eagle-webservice/src/main/webapp/app/public/js/ctrl/userProfileController.js
new file mode 100644
index 0000000..73bec7b
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/app/public/js/ctrl/userProfileController.js
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+// =============================================================
+// = User Profile List =
+// =============================================================
+damControllers.controller('userProfileListCtrl', function(globalContent, Site, damContent, $scope, $interval, Entities) {
+ globalContent.setConfig(damContent.config);
+ globalContent.pageSubTitle = Site.current().name;
+
+ $scope.common = common;
+ $scope.algorithms = [];
+
+ // ======================================== Algorithms ========================================
+ $scope.algorithmEntity = {};
+ Entities.queryEntities("AlertDefinitionService", {site: Site.current().name, dataSource: "userProfile"})._promise.then(function(data) {
+ $scope.algorithmEntity = common.getValueByPath(data, "data.obj[0]");
+ $scope.algorithmEntity.policy = common.parseJSON($scope.algorithmEntity.policyDef);
+ });
+
+ // ======================================= User profile =======================================
+ $scope.profileList = Entities.queryEntities("MLModelService", {site: Site.current().name}, ["user", "algorithm", "content", "version"]);
+ $scope.profileList._promise.then(function() {
+ var _algorithms = {};
+ var _users = {};
+
+ // Map user
+ $.each($scope.profileList, function(i, unit) {
+ _algorithms[unit.tags.algorithm] = unit.tags.algorithm;
+ var _user = _users[unit.tags.user] = _users[unit.tags.user] || {user: unit.tags.user};
+ _user[unit.tags.algorithm] = {
+ version: unit.version
+ };
+
+ // DE
+ if(unit.tags.algorithm === "DE") {
+ var _statistics = common.parseJSON(unit.content);
+ _statistics = common.getValueByPath(_statistics, "statistics", []);
+ _user[unit.tags.algorithm].topCommands = $.map(common.array.top(_statistics, "mean"), function(command) {
+ return command.commandName;
+ });
+ }
+ });
+
+ // Map algorithms
+ $scope.algorithms = $.map(_algorithms, function(algorithm) {
+ return algorithm;
+ }).sort();
+
+ $scope.profileList.splice(0);
+ $scope.profileList.push.apply($scope.profileList, common.map.toArray(_users));
+ });
+
+ // =========================================== Task ===========================================
+ $scope.tasks = [];
+ function _loadTasks() {
+ var _tasks = Entities.queryEntities("ScheduleTaskService", {
+ site: Site.current().name,
+ _pageSize: 100,
+ _duration: 1000 * 60 * 60 * 24 * 14,
+ __ETD: 1000 * 60 * 60 * 24
+ });
+ _tasks._promise.then(function() {
+ $scope.tasks.splice(0);
+ $scope.tasks.push.apply($scope.tasks, _tasks);
+
+ // Duration
+ $.each($scope.tasks, function(i, data) {
+ if(data.timestamp && data.updateTime) {
+ var _ms = (new moment(data.updateTime)).diff(new moment(data.timestamp));
+ var _d = moment.duration(_ms);
+ data._duration = Math.floor(_d.asHours()) + moment.utc(_ms).format(":mm:ss");
+ data.duration = _ms;
+ } else {
+ data._duration = "--";
+ }
+ });
+ });
+ }
+
+ $scope.runningTaskCount = function () {
+ return common.array.count($scope.tasks, "INITIALIZED", "status") +
+ common.array.count($scope.tasks, "PENDING", "status") +
+ common.array.count($scope.tasks, "EXECUTING", "status");
+ };
+
+ // Create task
+ $scope.updateTask = function() {
+ $.dialog({
+ title: "Confirm",
+ content: "Do you want to update now?",
+ confirm: true
+ }, function(ret) {
+ if(!ret) return;
+
+ var _entity = {
+ status: "INITIALIZED",
+ detail: "Newly created command",
+ tags: {
+ site: Site.current().name,
+ type: "USER_PROFILE_TRAINING"
+ },
+ timestamp: +new Date()
+ };
+ Entities.updateEntity("ScheduleTaskService", _entity, {timestamp: false})._promise.success(function(data) {
+ if(!Entities.dialog(data)) {
+ _loadTasks();
+ }
+ });
+ });
+ };
+
+ // Show detail
+ $scope.showTaskDetail = function(task) {
+ var _content = $("<pre>").text(task.detail);
+
+ var $mdl = $.dialog({
+ title: "Detail",
+ content: _content
+ });
+
+ // TODO: Remove task
+ _content.click(function(e) {
+ if(!e.ctrlKey) return;
+
+ $.dialog({
+ title: "Confirm",
+ content: "Remove this task?",
+ confirm: true
+ }, function(ret) {
+ if(!ret) return;
+
+ $mdl.modal('hide');
+ Entities.deleteEntity("ScheduleTaskService", task)._promise.then(function(data) {
+ _loadTasks();
+ });
+ });
+ });
+ };
+
+ _loadTasks();
+ var _loadInterval = $interval(_loadTasks, app.time.refreshInterval);
+ $scope.$on('$destroy',function(){
+ $interval.cancel(_loadInterval);
+ });
+});
+
+// =============================================================
+// = User Profile Detail =
+// =============================================================
+damControllers.controller('userProfileDetailCtrl', function(globalContent, Site, damContent, $scope, $routeParams, Entities) {
+ globalContent.setConfig(damContent.config);
+ globalContent.pageTitle = "User Profile";
+ globalContent.pageSubTitle = Site.current().name;
+ globalContent.navPath = ["User Profile", "Detail"];
+
+ $scope.user = $routeParams.user;
+
+ // User profile
+ $scope.profiles = {};
+ $scope.profileList = Entities.queryEntities("MLModelService", {site: Site.current().name, user: $scope.user});
+ $scope.profileList._promise.then(function() {
+ $.each($scope.profileList, function(i, unit) {
+ unit._content = common.parseJSON(unit.content);
+ $scope.profiles[unit.tags.algorithm] = unit;
+ });
+
+ // DE
+ if($scope.profiles.DE) {
+ $scope.profiles.DE._chart = {};
+
+ $scope.profiles.DE.estimates = {};
+ $.each($scope.profiles.DE._content, function(key, value) {
+ if(key !== "statistics") {
+ $scope.profiles.DE.estimates[key] = value;
+ }
+ });
+
+ var _meanList = [];
+ var _stddevList = [];
+ var _categoryList = [];
+
+ $.each($scope.profiles.DE._content.statistics, function(i, unit) {
+ _meanList[i] = unit.mean;
+ _stddevList[i] = unit.stddev;
+
+ _categoryList[i] = unit.commandName;
+ });
+ $scope.profiles.DE._chart.series = [
+ {
+ name: "mean",
+ data: _meanList
+ },
+ {
+ name: "stddev",
+ data: _stddevList
+ },
+ {
+ type: "category",
+ data: _categoryList
+ },
+ ];
+
+ // Percentage table list
+ $scope.profiles.DE.meanList = [];
+ var _total = common.array.sum($scope.profiles.DE._content.statistics, "mean");
+ $.each($scope.profiles.DE._content.statistics, function(i, unit) {
+ $scope.profiles.DE.meanList.push({
+ command: unit.commandName,
+ percentage: unit.mean / _total
+ });
+ });
+ }
+
+ // EigenDecomposition
+ if($scope.profiles.EigenDecomposition && $scope.profiles.EigenDecomposition._content.principalComponents) {
+ $scope.profiles.EigenDecomposition._chart = {
+ series: [],
+ };
+
+ $.each($scope.profiles.EigenDecomposition._content.principalComponents, function(z, grp) {
+ var _line = [];
+ $.each(grp, function(x, y) {
+ _line.push([x,y,z]);
+ });
+
+ $scope.profiles.EigenDecomposition._chart.series.push({
+ data: _line
+ });
+ });
+ }
+ });
+
+ // UI
+ $scope.showRawData = function(content) {
+ $.dialog({
+ title: "Raw Data",
+ content: $("<pre>").text(content)
+ });
+ };
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/2c3005c9/eagle-webservice/src/main/webapp/grunt.json
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/grunt.json b/eagle-webservice/src/main/webapp/grunt.json
new file mode 100644
index 0000000..78f4c30
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/grunt.json
@@ -0,0 +1,46 @@
+{
+ "concat": {
+ "js": {
+ "options": {
+ "separator": "\n"
+ },
+ "src": [
+ "node_modules/jquery/dist/jquery.min.js",
+ "node_modules/jquery-slimscroll/jquery.slimscroll.min.js",
+ "node_modules/bootstrap/dist/js/bootstrap.min.js",
+ "app/public/assets/bootstrap-components/js/bootstrap-components.min.js",
+ "node_modules/moment/min/moment-with-locales.min.js",
+ "node_modules/moment-timezone/builds/moment-timezone-with-data.min.js",
+ "node_modules/admin-lte/dist/js/app.min.js",
+ "node_modules/angular/angular.min.js",
+ "node_modules/angular-resource/angular-resource.min.js",
+ "node_modules/angular-route/angular-route.min.js",
+ "node_modules/angular-cookies/angular-cookies.min.js",
+ "node_modules/angular-ui-bootstrap/ui-bootstrap-tpls.min.js",
+ "node_modules/Flot/jquery.flot.js",
+ "node_modules/Flot/jquery.flot.stack.js",
+ "node_modules/Flot/jquery.flot.pie.js",
+ "node_modules/Flot/jquery.flot.time.js",
+ "node_modules/Flot/jquery.flot.crosshair.js",
+ "app/public/assets/flot/jquery.flot.tooltip.js",
+ "app/public/assets/flot/jquery.flot.legend.js",
+ "node_modules/d3/d3.min.js",
+
+ "tmp/public/js/scripts.min.js"
+ ],
+ "dest": "tmp/public/js/doc.js"
+ },
+ "css": {
+ "src": [
+ "node_modules/bootstrap/dist/css/bootstrap.min.css",
+ "app/public/assets/bootstrap-components/css/bootstrap-components.min.css",
+ "node_modules/font-awesome/css/font-awesome.min.css",
+ "node_modules/admin-lte/dist/css/AdminLTE.min.css",
+ "node_modules/admin-lte/dist/css/skins/skin-blue.min.css",
+
+ "tmp/public/css/styles.css"
+ ],
+ "dest": "tmp/public/css/styles.min.css"
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/2c3005c9/eagle-webservice/src/main/webapp/index.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/index.html b/eagle-webservice/src/main/webapp/index.html
index d5a4ade..a67650b 100755
--- a/eagle-webservice/src/main/webapp/index.html
+++ b/eagle-webservice/src/main/webapp/index.html
@@ -17,217 +17,12 @@
limitations under the License.
-->
-<html ng-app="eagleApp" ng-controller="MainCtrl">
+<html>
<head>
- <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta charset="UTF-8">
- <meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'>
- <link rel="shortcut icon" href="public/images/favicon.png">
-
- <title>Eagle</title>
-
- <!-- main -->
- <link rel="shortcut icon" type="image/png" href="public/images/favicon.png">
- <link rel="stylesheet" type="text/css" media="screen" href="public/css/main.css">
-
- <!-- jQuery -->
- <script src="public/assets/jquery/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/assets/jquery/jquery.slimscroll.min.js" type="text/javascript" charset="utf-8"></script>
-
- <!-- Bootstrap -->
- <link rel="stylesheet" type="text/css" media="screen" href="public/assets/bootstrap/css/bootstrap.min.css">
- <script src="public/assets/bootstrap/js/bootstrap.min.js" type="text/javascript" charset="utf-8"></script>
- <link rel="stylesheet" type="text/css" media="screen" href="public/assets/bootstrap-components/css/bootstrap-components.min.css">
- <script src="public/assets/bootstrap-components/js/bootstrap-components.min.js"></script>
-
- <!-- Moment -->
- <script src="public/assets/moment/moment-with-locales.min.js"></script>
- <script src="public/assets/moment/moment-timezone-with-data.min.js"></script>
-
- <!-- FastClick -->
- <script src="public/assets/fastclick/fastclick.min.js"></script>
-
- <!-- Font Awesome Icons -->
- <link href="public/assets/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
-
- <!-- AdminLTE -->
- <!-- Theme style -->
- <link href="public/assets/adminLTE/dist/css/AdminLTE.min.css" rel="stylesheet" type="text/css" />
- <link href="public/assets/adminLTE/dist/css/skins/skin-blue.min.css" rel="stylesheet" type="text/css" />
- <script src="public/assets/adminLTE/dist/js/app.min.js"></script>
-
- <!-- Angular -->
- <script src="public/assets/angular/angular.min.js"></script>
- <script src="public/assets/angular/angular-resource.min.js"></script>
- <script src="public/assets/angular/angular-route.min.js"></script>
- <script src="public/assets/angular/angular-cookies.min.js"></script>
- <script src="public/assets/angular/angular-animate.min.js"></script>
- <script src="public/assets/angular/ui-bootstrap-tpls-0.13.4.min.js"></script>
-
- <!-- Flot -->
- <script src="public/assets/flot/excanvas.min.js"></script>
- <script src="public/assets/flot/jquery.flot.min.js"></script>
- <script src="public/assets/flot/jquery.flot.stack.min.js"></script>
- <script src="public/assets/flot/jquery.flot.pie.min.js"></script>
- <script src="public/assets/flot/jquery.flot.time.min.js"></script>
- <script src="public/assets/flot/jquery.flot.crosshair.min.js"></script>
- <script src="public/assets/flot/jquery.flot.tooltip.js"></script>
- <script src="public/assets/flot/jquery.flot.legend.js"></script>
-
- <!-- d3 -->
- <script src="public/assets/d3/d3.min.js"></script>
-
- <!-- Customized -->
- <script src="public/js/app.ui.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/components/main.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/components/sortTable.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/components/tabs.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/components/file.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/components/charts.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/components/charts/bar.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/components/charts/line3d.js" type="text/javascript" charset="utf-8"></script>
-
- <script src="public/js/common.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/config.js" type="text/javascript" charset="utf-8"></script>
-
- <script src="public/js/app.time.js" type="text/javascript" charset="utf-8"></script>
-
- <script src="public/js/app.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/ctrl/damController.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/ctrl/policyController.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/ctrl/siteController.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/ctrl/streamController.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/ctrl/alertController.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/ctrl/sensitivityController.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/ctrl/userProfileController.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/ctrl/authController.js" type="text/javascript" charset="utf-8"></script>
+ <script>
+ window.location.href = "ui";
+ </script>
</head>
- <body class="skin-blue sidebar-mini" ng-class="{'no-sidebar' : globalContent.hideSidebar}">
- <!-- Site wrapper -->
- <div class="wrapper">
- <header class="main-header">
- <a href="#/" class="logo">
- <span class="logo-mini">DAM</span>
- <span class="logo-lg">Eagle <small>Data Activity Monitoring</small></span>
- </a>
- <!-- Header Navbar: style can be found in header.less -->
- <nav class="navbar navbar-static-top" role="navigation">
- <!-- Sidebar toggle button-->
- <a href="#" ng-hide="globalContent.hideSidebar" class="sidebar-toggle" data-toggle="offcanvas" role="button">
- <span class="sr-only">Toggle navigation</span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- </a>
- <div class="navbar-custom-menu">
- <ul class="nav navbar-nav">
- <!-- Site -->
- <li class="dropdown" ng-show="!globalContent.hideSite && !globalContent.lockSite">
- <a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
- <i class="fa fa-server"></i>
- {{site.current().name}}
- <i class="fa fa-caret-down"></i>
- </a>
- <ul class="dropdown-menu">
- <li ng-repeat="sites in site.list">
- <a ng-click="site.current(sites);">
- <span class="fa fa-database"></span> {{sites.name}}
- </a>
- </li>
- <!--li role="separator" class="divider"></li>
- <li>
- <a href="#/dam/siteList"><span class="fa fa-cog"></span>Config Sites</a>
- </li-->
- </ul>
- </li>
- <li class="dropdown" ng-show="globalContent.lockSite">
- <a>
- <i class="fa fa-server"></i>
- {{site.current().name}}
- </a>
- </li>
-
- <!-- User -->
- <li class="dropdown user user-menu" ng-hide="globalContent.hideUser">
- <a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
- <i class="fa fa-user"></i>
- {{auth.userProfile.username}}
- </a>
- <!--ul class="dropdown-menu">
- <li>
- <a ng-click="logout();">
- <span class="fa fa-sign-out"></span> Logout
- </a>
- </li>
- </ul-->
- <ul class="dropdown-menu">
- <!-- User image -->
- <li class="user-header">
- <span class="img-circle">
- <span class="fa fa-user" alt="User Image"></span>
- </span>
- <p>
- {{auth.userProfile.username}}
- <small>
- <span ng-repeat="role in auth.userProfile.authorities">{{role.authority}} </span>
- </small>
- </p>
- </li>
- <!-- Menu Footer-->
- <li class="user-footer">
- <div class="pull-right">
- <a ng-click="logout();" class="btn btn-default btn-flat">Sign out</a>
- </div>
- </li>
- </ul>
- </li>
- </ul>
- </div>
- </nav>
- </header>
-
- <!-- =============================================== -->
- <!-- Left side column. contains the side bar -->
- <aside class="main-sidebar" ng-hide="globalContent.hideSidebar">
- <!-- side bar: style can be found in sidebar.less -->
- <section class="sidebar">
- <ul class="sidebar-menu">
- <li ng-repeat="page in globalContent.pageList" ng-class="getNavClass(page)" ng-show="getNavVisible(page)">
- <a href="{{page.url}}">
- <i class="fa fa-{{page.icon}}"></i> <span>{{page.title}}</span>
- </a>
- </li>
- </ul>
- </section>
- <!-- /.sidebar -->
- </aside>
-
- <!-- =============================================== -->
- <!-- Right side column. Contains the navbar and content of the page -->
- <div class="content-wrapper">
- <!-- Content Header (Page header) -->
- <section class="content-header" ng-hide="globalContent.hideSidebar">
- <h1>
- <span class="pageTitle">{{globalContent.pageTitle}}</span>
- <small class="pageSubTitle">{{globalContent.pageSubTitle}}</small>
- </h1>
-
- <ol class="breadcrumb">
- <li ng-repeat="nav in globalContent.navPath">
- <a ng-href="{{$last ? '' : globalContent.navMapping[nav]}}">
- <span class="fa fa-home" ng-show="$first"></span>
- {{nav}}
- </a>
- </li>
- </ol>
- </section>
-
- <!-- Main content -->
- <section class="content">
- <div id="content" ng-view></div>
- </section><!-- /.content -->
- </div><!-- /.content-wrapper -->
- </div><!-- ./wrapper -->
+ <body>
</body>
</html>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/2c3005c9/eagle-webservice/src/main/webapp/package.json
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/package.json b/eagle-webservice/src/main/webapp/package.json
new file mode 100644
index 0000000..f07a6d3
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/package.json
@@ -0,0 +1,47 @@
+{
+ "name": "ApacheEagleWebApp",
+ "description": "Apache Eagle Web Application",
+ "author": "ApacheEagle",
+ "repository": {
+ "type:": "git",
+ "url": "https://github.com/apache/incubator-kylin.git"
+ },
+ "license": "Apache-2.0",
+ "dependencies": {
+ "jquery" : "1.11.3",
+ "bootstrap" : "3.3.5",
+ "moment" : "2.10.6",
+ "moment-timezone" : "0.4.1",
+ "font-awesome" : "4.4.0",
+ "admin-lte" : "2.3.2",
+ "angular" : "1.4.7",
+ "angular-resource" : "1.4.7",
+ "angular-route" : "1.4.7",
+ "angular-cookies" : "1.4.7",
+ "angular-animate" : "1.4.7",
+ "angular-ui-bootstrap" : "0.14.3",
+ "Flot": "git+https://github.com/flot/flot.git#v0.8.3",
+ "d3" : "3.5.9",
+ "jquery-slimscroll": "1.3.6"
+ },
+
+ "devDependencies": {
+ "grunt": "~0.4.5",
+ "grunt-cli": "~0.1.13",
+
+ "grunt-contrib-jshint": "~0.10.0",
+ "grunt-contrib-nodeunit": "~0.4.1",
+
+ "grunt-regex-replace": "~0.2.6",
+ "grunt-contrib-clean": "~0.7.0",
+ "grunt-contrib-uglify": "~0.5.0",
+ "grunt-contrib-concat": "~0.5.1",
+ "grunt-contrib-cssmin": "~0.14.0",
+ "grunt-contrib-copy": "~0.8.2",
+ "grunt-htmlrefs": "~0.5.0"
+ },
+
+ "scripts": {
+ "grunt": "grunt"
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/2c3005c9/eagle-webservice/src/main/webapp/partials/dam/alertDetail.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/partials/dam/alertDetail.html b/eagle-webservice/src/main/webapp/partials/dam/alertDetail.html
deleted file mode 100755
index 9b0ec01..0000000
--- a/eagle-webservice/src/main/webapp/partials/dam/alertDetail.html
+++ /dev/null
@@ -1,61 +0,0 @@
-<!--
- 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.
- -->
-<div class="box box-info">
- <div class="box-header with-border">
- <h3 class="box-title" id="policyId">
- {{alert.tags.policyId}}
- <small>{{common.format.date(alert.timestamp)}}</small>
- </h3>
- </div><!-- /.box-header -->
-
- <div class="box-body">
- <a class="btn btn-primary pull-right" href="#/dam/policyDetail/?policy={{alert.tags.policyId}}&site={{alert.tags.site}}&executor={{alert.tags.alertExecutorId}}">View Policy</a>
-
- <div class="inline-group">
- <dl><dt>Site</dt><dd>{{alert.tags.site}}</dd></dl>
- <dl><dt>Data Source</dt><dd>{{alert.tags.dataSource}}</dd></dl>
- </div>
- <div class="inline-group">
- <dl><dt>Alert Time</dt><dd>{{common.format.date(alert.timestamp)}}</dd></dl>
- <dl><dt>Message Time</dt><dd>{{common.format.date(alert.alertContext.properties.timestamp)}}</dd></dl>
- </div>
- <!--div class="inline-group">
- <dl><dt>Severity</dt><dd>{{alert.alertContext.properties.severity}}</dd></dl>
- </div-->
- <div class="inline-group">
- <dl><dt>Stream Name</dt><dd>{{alert.tags.sourceStreams}}</dd></dl>
- </div>
- <div class="inline-group">
- <dl><dt>Alert Source</dt><dd>{{alert.tags.alertSource}}</dd></dl>
- </div>
- <div class="inline-group">
- <dl><dt>User</dt><dd>{{alert.alertContext.properties.user}}</dd></dl>
- <dl><dt>Host</dt><dd>{{alert.alertContext.properties.host}}</dd></dl>
- </div>
- <div class="inline-group">
- <dl><dt>Event</dt><dd>{{alert.alertContext.properties.alertEvent}}</dd></dl>
- </div>
- <div class="inline-group">
- <dl><dt>Message</dt><dd>{{alert.alertContext.properties.alertMessage}}</dd></dl>
- </div>
- </div><!-- /.box-body -->
-
- <div class="overlay" ng-hide="alertList._promise.$$state.status === 1;">
- <i class="fa fa-refresh fa-spin"></i>
- </div>
-</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/2c3005c9/eagle-webservice/src/main/webapp/partials/dam/alertList.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/partials/dam/alertList.html b/eagle-webservice/src/main/webapp/partials/dam/alertList.html
deleted file mode 100755
index d5492ca..0000000
--- a/eagle-webservice/src/main/webapp/partials/dam/alertList.html
+++ /dev/null
@@ -1,83 +0,0 @@
-<!--
- 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.
- -->
-<div class="box box-primary">
- <div class="box-header with-border">
- <i class="fa fa-list-alt"> </i>
- <h3 class="box-title">
- {{dataSource || "All Alerts"}}
- <div class="btn-group">
- <button class="btn btn-box-tool dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
- <i class="fa fa-caret-down"></i>
- </button>
- <ul id="programList" class="dropdown-menu" role="menu">
- <li ng-repeat="dataSrc in site.current().dataSrcList">
- <a href="#/dam/alertList/{{dataSrc.tags.dataSource}}">{{dataSrc.tags.dataSource}}</a>
- </li>
- <li class="divider"></li>
- <li>
- <a href="#/dam/alertList">All Policy List</a>
- </li>
- </ul>
- </div>
- </h3>
- </div>
- <div class="box-body">
- <p ng-show="!alertList.ready">
- <span class="fa fa-refresh fa-spin"> </span>
- Loading...
- </p>
-
- <div sorttable source="alertList" sort="-timestamp">
- <table class="table table-bordered" ng-non-bindable>
- <thead>
- <tr>
- <th width="170" sortpath="timestamp">Alert Time</th>
- <th width="170" sortpath="alertContext.properties.timestamp">Message Time</th>
- <th width="105" sortpath="tags.dataSource">Data Source</th>
- <!--th width="70" sortpath="severity">Type</th-->
- <th width="150" sortpath="tags.policyId">Policy Name</th>
- <th width="60" sortpath="alertContext.properties.user">User</th>
- <th width="150" sortpath="alertContext.properties.host">Host</th>
- <th sortpath="alertContext.properties.emailMessage">Description</th>
- <th width="50"> </th>
- </tr>
- </thead>
- <tbody>
- <tr ng-class="{info : item.__new}">
- <td>{{common.format.date(item.timestamp)}}</td>
- <td>{{common.format.date(item.alertContext.properties.timestamp)}}</td>
- <td>{{item.tags.dataSource}}</td>
- <!--td>{{item.severity}}</td-->
- <td class="text-nowrap">
- <a class="fa fa-share-square-o" ng-show="item.tags.policyId"
- href="#/dam/policyDetail/?policy={{item.tags.policyId}}&site={{item.tags.site}}&executor={{item.tags.alertExecutorId}}"> </a>
- {{item.tags.policyId}}
- </td>
- <td>{{item.alertContext.properties.user}}</td>
- <td>{{item.alertContext.properties.host}}</td>
- <td>{{item.alertContext.properties.alertMessage}}</td>
- <td><a href="#/dam/alertDetail/{{item.encodedRowkey}}">Detail</a></td>
- </tr>
- </tbody>
- </table>
- </div>
-
- </div>
- <!--div class="box-footer clearfix">
- </div-->
-</div>