monasca-ui/monitoring/static/monitoring/js/directives.js

371 lines
11 KiB
JavaScript

/*
* Copyright 2016 FUJITSU LIMITED
*
* Licensed 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';
angular
.module('monitoring.directives', [
'horizon.framework.widgets',
'monitoring.filters',
'monitoring.services',
'gettext'
])
.directive('monAlarmExpression',
['monitoringApp.staticPath', monAlarmExpressionsDirective]
)
.directive('monAlarmSubExpression',
['monitoringApp.staticPath', monAlarmSubExpressionDirective]
);
function monAlarmExpressionsDirective(staticPath){
return {
restrict: 'E',
scope: {
metrics: '=metrics',
functions: '=functions',
operators: '=operators',
comparators: '=comparators',
connectable: '=connectable'
},
templateUrl: staticPath + 'expression/expression.tpl.html',
controller: ['$q', '$scope', 'monExpressionBuilder', AlarmExpressionController],
controllerAs: 'vm',
bindToController: true
};
function AlarmExpressionController($q, $scope, monExpressionBuilder) {
// private
var vm = this,
deterministic = false,
matchBy = undefined;
// scope
vm.expression = '';
vm.subExpressions = undefined;
vm.expressionValid = true;
// api
vm.touch = touch;
vm.addExpression = addExpression;
vm.removeExpression = removeExpression;
vm.reorderExpression = reorderExpression;
// listen
$scope.$on('$destroy', destroy);
// init
$scope.$applyAsync(init);
function addExpression($event, $index) {
if ($event) {
$event.preventDefault();
}
vm.subExpressions.splice($index, 0, {});
if ($index >= 1 && vm.subExpressions[$index - 1].$valid) {
// hide previous expression
// if it is valid
vm.subExpressions[$index - 1]['preview'] = true;
}
applyConnectable();
return true;
}
function removeExpression($event, index) {
if ($event) {
$event.preventDefault();
}
vm.subExpressions.splice(index, 1);
applyConnectable();
touch();
if (vm.subExpressions.length === 0) {
vm.expression = '';
vm.subExpressions.push({});
}
return true;
}
function reorderExpression($event, which, where) {
$event.preventDefault();
vm.subExpressions[where]['op'] = [
vm.subExpressions[which]['op'],
vm.subExpressions[which]['op'] = vm.subExpressions[where]['op']
][0];
vm.subExpressions[where] = vm.subExpressions.splice(which, 1, vm.subExpressions[where])[0];
applyConnectable();
return true;
}
function touch() {
var hasInvalid = false;
expression = undefined;
matchBy = [];
deterministic = true;
angular.forEach(vm.subExpressions, subExpressionIt);
if (hasInvalid) {
expression = undefined;
} else {
$scope.$emit('mon_match_by_changed', matchBy.sort());
$scope.$emit('mon_deterministic_changed', deterministic);
expression = monExpressionBuilder.asString(vm.subExpressions, true);
// change preview only if valid
vm.expression = expression;
}
// update that always, regardless if it's valid or not
// for invalid case that will reset input's value to empty
// disallowing form to be accepted by django
$('#expression').val(expression);
vm.expressionValid = !hasInvalid;
return true;
function subExpressionIt(expr) {
if (!expr.$valid) {
return !(hasInvalid = true);
}
angular.forEach(expr.matchBy || [], function it(mb) {
if(matchBy.indexOf(mb) < 0) {
matchBy.push(mb);
}
});
deterministic = deterministic && (expr.deterministic || false);
return true;
}
}
function init() {
if(vm.metrics.length) {
vm.subExpressions = [];
vm.matchBy = [];
addExpression(undefined, 0);
}
}
function destroy() {
delete vm.metrics;
delete vm.expression;
delete vm.subExpressions;
delete vm.deterministic;
}
function applyConnectable() {
var count = vm.subExpressions.length;
switch(count) {
case 1:
vm.subExpressions[0]['connectable'] = false;
break;
default: {
angular.forEach(vm.subExpressions, function(expr, index) {
expr.connectable = index >= 1 && index < vm.subExpressions.length;
if (!expr.connectable) {
delete expr['op'];
}
});
}
}
}
}
}
function monAlarmSubExpressionDirective(staticPath) {
return {
restrict: 'E',
require: '^monAlarmExpression',
scope: {
metrics: '=metrics',
functions: '=functions',
comparators: '=comparators',
operators: '=operators',
model: '=subExpression',
connectable: '=connectable',
preview: '=preview'
},
templateUrl: staticPath + 'expression/sub-expression.tpl.html',
link: linkFn,
controller: ['$q', '$scope', 'monExpressionBuilder', AlarmSubExpressionController],
controllerAs: 'vm',
bindToController: true
};
function linkFn(scope, el, attrs, monAlarmExpressions) {
el.on('$destroy', (function(){
var watcher = scope.$watch('vm.model', function(expr, oldExpr) {
if (expr !== oldExpr) {
monAlarmExpressions.touch();
}
}, true);
return function destroyer() {
watcher();
};
}()));
}
function AlarmSubExpressionController($q, $scope, monExpressionBuilder) {
var vm = this;
vm.tags = [];
vm.matchingMetrics = [];
// api
vm.possibleDimensions = possibleDimensions;
vm.onMetricChanged = onMetricChanged;
vm.onDimensionsUpdated = onDimensionsUpdated;
vm.updateExpression = updateExpression;
vm.resetExpression = resetExpression;
// init
$scope.$on('$destroy', destroyerFactory());
function opRemoverListener(nval) {
if (vm.model && 'op' in vm.model && !nval) {
delete vm.model['op'];
}
}
function destroyerFactory() {
var watcher = $scope.$watch('vm.model.connectable', opRemoverListener, true);
return function destroyer() {
watcher();
delete vm.tags;
delete vm.matchingMetrics;
delete vm.model;
}
}
function updateExpression() {
var dim = [],
formController = $scope.$$childHead.subExpressionForm;
vm.model.$valid = !formController.$invalid;
if (vm.model.$valid) {
if (vm.tags.length > 0) {
angular.forEach(vm.tags, function(value, key) {
dim.push(value['text']);
});
vm.model.dimensions = dim;
} else {
vm.model.dimensions = [];
}
}
}
function resetExpression() {
vm.matchingMetrics = [];
vm.tags = [];
}
function possibleDimensions(query) {
return $q(function(resolve) {
var dim = {},
dimList = [];
angular.forEach(vm.matchingMetrics, function(value, name) {
for (var key in value.dimensions) {
if (value.dimensions.hasOwnProperty(key)) {
var dimStr = key + "=" + value.dimensions[key];
if (dimStr.indexOf(query) === 0) {
dim[dimStr] = dimStr;
}
}
}
});
angular.forEach(dim, function(value, name) {
dimList.push(value);
});
resolve(dimList);
});
}
function onDimensionsUpdated() {
onMetricChanged(vm.model.metric);
}
function onMetricChanged(metric) {
handleMetricChanged(metric);
updateExpression();
}
function handleMetricChanged(metric) {
var mm = [],
matchBy = [],
tags = vm.tags || [];
angular.forEach(vm.metrics, function(value, key) {
if (value.name === metric.name) {
var match = true;
for (var i = 0; i < tags.length; i++) {
var vals = tags[i]['text'].split('=');
if (value.dimensions[vals[0]] !== vals[1]) {
match = false;
break;
}
}
if (match) {
mm.push(value);
}
}
});
angular.forEach(mm, function(value, key){
angular.forEach(value.dimensions, function(value, key){
if(matchBy.indexOf(key) < 0){
matchBy.push(key);
}
});
});
vm.matchingMetrics = mm;
vm.model.matchBy = matchBy.sort();
}
}
}