All Downloads are FREE. Search and download functionalities are using the official Maven repository.

app.services.dashboard.js Maven / Gradle / Ivy

The newest version!
define([
  'angular',
  'jquery',
  'kbn',
  'lodash',
  'config',
  'moment',
  'modernizr',
  'filesaver',
  'blob'
],
function (angular, $, kbn, _, config, moment, Modernizr) {
  'use strict';

  var module = angular.module('kibana.services');

  module.service('dashboard', function(
    $routeParams, $http, $rootScope, $injector, $location, $timeout,
    ejsResource, timer, kbnIndex, alertSrv, esVersion, esMinVersion
  ) {
    // A hash of defaults to use when loading a dashboard

    var _dash = {
      title: "",
      style: "dark",
      editable: true,
      failover: false,
      panel_hints: true,
      rows: [],
      pulldowns: [
        {
          type: 'query',
        },
        {
          type: 'filtering'
        }
      ],
      nav: [
        {
          type: 'timepicker'
        }
      ],
      services: {},
      loader: {
        save_gist: false,
        save_elasticsearch: true,
        save_local: true,
        save_default: true,
        save_temp: true,
        save_temp_ttl_enable: true,
        save_temp_ttl: '30d',
        load_gist: false,
        load_elasticsearch: true,
        load_elasticsearch_size: 20,
        load_local: false,
        hide: false
      },
      index: {
        interval: 'none',
        pattern: '_all',
        default: 'INDEX_MISSING',
        warm_fields: true
      },
      refresh: false
    };

    // An elasticJS client to use
    var ejs = ejsResource(config);

    var gist_pattern = /(^\d{5,}$)|(^[a-z0-9]{10,}$)|(gist.github.com(\/*.*)\/[a-z0-9]{5,}\/*$)/;

    // Store a reference to this
    var self = this;
    var filterSrv,querySrv;

    this.current = _.clone(_dash);
    this.last = {};
    this.availablePanels = [];

    $rootScope.$on('$routeChangeSuccess',function(){
      // Clear the current dashboard to prevent reloading
      self.current = {};
      self.indices = [];
      esVersion.isMinimum().then(function(isMinimum) {
        if(_.isUndefined(isMinimum)) {
          return;
        }
        if(isMinimum) {
          route();
        } else {
          alertSrv.set('Upgrade Required',"Your version of Elasticsearch is too old. Kibana requires" +
            " Elasticsearch " + esMinVersion + " or above.", "error");
        }
      });
    });

    var route = function() {
      // Is there a dashboard type and id in the URL?
      if(!(_.isUndefined($routeParams.kbnType)) && !(_.isUndefined($routeParams.kbnId))) {
        var _type = $routeParams.kbnType;
        var _id = $routeParams.kbnId;

        switch(_type) {
        case ('elasticsearch'):
          self.elasticsearch_load('dashboard',_id);
          break;
        case ('temp'):
          self.elasticsearch_load('temp',_id);
          break;
        case ('file'):
          self.file_load(_id);
          break;
        case('script'):
          self.script_load(_id);
          break;
        case('local'):
          self.local_load();
          break;
        default:
          $location.path(config.default_route);
        }
      // No dashboard in the URL
      } else {
        // Check if browser supports localstorage, and if there's an old dashboard. If there is,
        // inform the user that they should save their dashboard to Elasticsearch and then set that
        // as their default
        if (Modernizr.localstorage) {
          if(!(_.isUndefined(window.localStorage['dashboard'])) && window.localStorage['dashboard'] !== '') {
            $location.path(config.default_route);
            alertSrv.set('Saving to browser storage has been replaced',' with saving to Elasticsearch.'+
              ' Click here to load your old dashboard anyway.');
          } else if(!(_.isUndefined(window.localStorage.kibanaDashboardDefault))) {
            $location.path(window.localStorage.kibanaDashboardDefault);
          } else {
            $location.path(config.default_route);
          }
        // No? Ok, grab the default route, its all we have now
        } else {
          $location.path(config.default_route);
        }
      }
    };

    // Since the dashboard is responsible for index computation, we can compute and assign the indices
    // here before telling the panels to refresh
    this.refresh = function() {
      if(self.current.index.interval !== 'none') {
        if(_.isUndefined(filterSrv)) {
          return;
        }
        if(filterSrv.idsByType('time').length > 0) {
          var _range = filterSrv.timeRange('last');
          kbnIndex.indices(_range.from,_range.to,
            self.current.index.pattern,self.current.index.interval
          ).then(function (p) {
            if(p.length > 0) {
              self.indices = p;
            } else {
              // Option to not failover
              if(self.current.failover) {
                self.indices = [self.current.index.default];
              } else {
                // Do not issue refresh if no indices match. This should be removed when panels
                // properly understand when no indices are present
                alertSrv.set('No results','There were no results because no indices were found that match your'+
                  ' selected time span','info',5000);
                return false;
              }
            }
            // Don't resolve queries until indices are updated
            querySrv.resolve().then(function(){$rootScope.$broadcast('refresh');});
          });
        } else {
          if(self.current.failover) {
            self.indices = [self.current.index.default];
            querySrv.resolve().then(function(){$rootScope.$broadcast('refresh');});
          } else {
            alertSrv.set("No time filter",
              'Timestamped indices are configured without a failover. Waiting for time filter.',
              'info',5000);
          }
        }
      } else {
        self.indices = [self.current.index.default];
        querySrv.resolve().then(function(){$rootScope.$broadcast('refresh');});
      }
    };

    var dash_defaults = function(dashboard) {
      _.defaults(dashboard,_dash);
      _.defaults(dashboard.index,_dash.index);
      _.defaults(dashboard.loader,_dash.loader);
      return _.cloneDeep(dashboard);
    };

    this.dash_load = function(dashboard) {
      // Cancel all timers
      timer.cancel_all();

      // Make sure the dashboard being loaded has everything required
      dashboard = dash_defaults(dashboard);

      // If not using time based indices, use the default index
      if(dashboard.index.interval === 'none') {
        self.indices = [dashboard.index.default];
      }

      // Set the current dashboard
      self.current = _.clone(dashboard);

      // Delay this until we're sure that querySrv and filterSrv are ready
      $timeout(function() {
        // Ok, now that we've setup the current dashboard, we can inject our services
        if(!_.isUndefined(self.current.services.query)) {
          querySrv = $injector.get('querySrv');
          querySrv.init();
        }
        if(!_.isUndefined(self.current.services.filter)) {
          filterSrv = $injector.get('filterSrv');
          filterSrv.init();
        }
      },0).then(function() {
        // Call refresh to calculate the indices and notify the panels that we're ready to roll
        self.refresh();
      });

      if(dashboard.refresh) {
        self.set_interval(dashboard.refresh);
      }

      // Set the available panels for the "Add Panel" drop down
      self.availablePanels = _.difference(config.panel_names,
        _.pluck(_.union(self.current.nav,self.current.pulldowns),'type'));

      // Take out any that we're not allowed to add from the gui.
      self.availablePanels = _.difference(self.availablePanels,config.hidden_panels);

      return true;
    };

    this.gist_id = function(string) {
      if(self.is_gist(string)) {
        return string.match(gist_pattern)[0].replace(/.*\//, '');
      }
    };

    this.is_gist = function(string) {
      if(!_.isUndefined(string) && string !== '' && !_.isNull(string.match(gist_pattern))) {
        return string.match(gist_pattern).length > 0 ? true : false;
      } else {
        return false;
      }
    };

    this.to_file = function() {
      var blob = new Blob([angular.toJson(self.current,true)], {type: "application/json;charset=utf-8"});
      // from filesaver.js
      window.saveAs(blob, self.current.title+"-"+new Date().getTime());
      return true;
    };

    this.set_default = function(route) {
      if (Modernizr.localstorage) {
        // Purge any old dashboards
        if(!_.isUndefined(window.localStorage['dashboard'])) {
          delete window.localStorage['dashboard'];
        }
        window.localStorage.kibanaDashboardDefault = route;
        return true;
      } else {
        return false;
      }
    };

    this.purge_default = function() {
      if (Modernizr.localstorage) {
        // Purge any old dashboards
        if(!_.isUndefined(window.localStorage['dashboard'])) {

          delete window.localStorage['dashboard'];
        }
        delete window.localStorage.kibanaDashboardDefault;
        return true;
      } else {
        return false;
      }
    };

    // TOFIX: Pretty sure this breaks when you're on a saved dashboard already
    this.share_link = function(title,type,id) {
      return {
        location  : window.location.href.substr(0, window.location.href.indexOf('#')),
        type      : type,
        id        : id,
        link      : window.location.href.substr(0, window.location.href.indexOf('#'))+"#dashboard/"+type+"/"+encodeURIComponent(id),
        title     : title
      };
    };

    var renderTemplate = function(json,params) {
      var _r;
      _.templateSettings = {interpolate : /\{\{(.+?)\}\}/g};
      var template = _.template(json);
      var rendered = template({ARGS:params});
      try {
        _r = angular.fromJson(rendered);
      } catch(e) {
        _r = false;
      }
      return _r;
    };

    this.local_load = function() {
      var dashboard = JSON.parse(window.localStorage['dashboard']);
      dashboard.rows.unshift({
        height: "30",
        title: "Deprecation Notice",
        panels: [
          {
            title: 'WARNING: Legacy dashboard',
            type: 'text',
            span: 12,
            mode: 'html',
            content: 'This dashboard has been loaded from the browsers local cache. If you use '+
            'another brower or computer you will not be able to access it! '+
            '\n\n  

Good news!

Kibana'+ ' now stores saved dashboards in Elasticsearch. Click the '+ 'button in the top left to save this dashboard. Then select "Set as Home" from'+ ' the "advanced" sub menu to automatically use the stored dashboard as your Kibana '+ 'landing page afterwards'+ '

Tip: You may with to remove this row before saving!' } ] }); self.dash_load(dashboard); }; this.file_load = function(file) { return $http({ url: "app/dashboards/"+file.replace(/\.(?!json)/,"/")+'?' + new Date().getTime(), method: "GET", transformResponse: function(response) { return renderTemplate(response,$routeParams); } }).then(function(result) { if(!result) { return false; } self.dash_load(dash_defaults(result.data)); return true; },function() { alertSrv.set('Error',"Could not load dashboards/"+file+". Please make sure it exists" ,'error'); return false; }); }; this.elasticsearch_load = function(type,id) { var successcb = function(data) { var response = renderTemplate(angular.fromJson(data).dashboard, $routeParams); self.dash_load(response); }; var errorcb = function(data, status) { if(status === 0) { alertSrv.set('Error',"Could not contact Elasticsearch at "+ejs.config.host+ ". Please ensure that Elasticsearch is reachable from your system." ,'error'); } else { alertSrv.set('Error',"Could not find "+id+". If you"+ " are using a proxy, ensure it is configured correctly",'error'); } return false; }; ejs.getSource(config.kibana_index, type, id).then(successcb, errorcb); }; this.script_load = function(file) { return $http({ url: "app/dashboards/"+file.replace(/\.(?!js)/,"/"), method: "GET", transformResponse: function(response) { /*jshint -W054 */ var _f = new Function('ARGS','kbn','_','moment','window','document','angular','require','define','$','jQuery',response); return _f($routeParams,kbn,_,moment); } }).then(function(result) { if(!result) { return false; } self.dash_load(dash_defaults(result.data)); return true; },function() { alertSrv.set('Error', "Could not load scripts/"+file+". Please make sure it exists and returns a valid dashboard" , 'error'); return false; }); }; this.elasticsearch_save = function(type,title,ttl) { // Clone object so we can modify it without influencing the existing obejct var save = _.clone(self.current); var id; // Change title on object clone if (type === 'dashboard') { id = save.title = _.isUndefined(title) ? self.current.title : title; } // Create request with id as title. Rethink this. var indexSource = { user: 'guest', group: 'guest', title: save.title, dashboard: angular.toJson(save) }; return ejs.doIndex(config.kibana_index,type,id, indexSource, type === 'temp' && ttl ? ttl : undefined).then( // Success function(result) { if(type === 'dashboard') { $location.path('/dashboard/elasticsearch/'+title); } return result; }, // Failure function() { return false; } ); }; this.elasticsearch_delete = function(id) { return ejs.doDelete(config.kibana_index,'dashboard',id).then( // Success function(result) { return result; }, // Failure function() { return false; } ); }; this.elasticsearch_list = function(query,count) { var request = ejs.Request(); var ejsQuery = ejs.BoolQuery(); if(query) { ejsQuery = ejsQuery.must(ejs.QueryStringQuery(query).defaultField('title')); } ejsQuery = ejsQuery.must(ejs.MatchQuery('_type', 'dashboard')); request = request.query(ejsQuery); return ejs.doSearch(config.kibana_index, request, count).then( // Success function(result) { return result; }, // Failure function() { return false; } ); }; this.save_gist = function(title,dashboard) { var save = _.clone(dashboard || self.current); save.title = title || self.current.title; return $http({ url: "https://api.github.com/gists", method: "POST", data: { "description": save.title, "public": false, "files": { "kibana-dashboard.json": { "content": angular.toJson(save,true) } } } }).then(function(data) { return data.data.html_url; }, function() { return false; }); }; this.gist_list = function(id) { return $http.jsonp("https://api.github.com/gists/"+id+"?callback=JSON_CALLBACK" ).then(function(response) { var files = []; _.each(response.data.data.files,function(v) { try { var file = JSON.parse(v.content); files.push(file); } catch(e) { return false; } }); return files; }, function() { return false; }); }; this.start_scheduled_refresh = function (after_ms) { timer.cancel(self.refresh_timer); self.refresh_timer = timer.register($timeout(function () { self.start_scheduled_refresh(after_ms); self.refresh(); }, after_ms)); }; this.cancel_scheduled_refresh = function () { timer.cancel(self.refresh_timer); }; this.set_interval = function (interval) { self.current.refresh = interval; if (interval) { var _i = kbn.interval_to_ms(interval); this.start_scheduled_refresh(_i); } else { this.cancel_scheduled_refresh(); } }; }); });




© 2015 - 2025 Weber Informatics LLC | Privacy Policy