瀏覽代碼

Merge branch 'master' into develop

Torkel Ödegaard 8 年之前
父節點
當前提交
93d21bec75
共有 43 個文件被更改,包括 1568 次插入2357 次删除
  1. 0 109
      Dockerfile
  2. 0 22
      LICENSE.txt
  3. 1 0
      conf/defaults.ini
  4. 1 0
      conf/sample.ini
  5. 31 0
      docs/sources/http_api/alerting.md
  6. 2 2
      docs/sources/http_api/snapshot.md
  7. 3 0
      docs/sources/installation/configuration.md
  8. 2 2
      latest.json
  9. 6 2
      pkg/components/imguploader/gcsuploader.go
  10. 2 1
      pkg/components/imguploader/imguploader.go
  11. 28 3
      pkg/log/file.go
  12. 44 0
      pkg/log/file_test.go
  13. 2 0
      public/app/core/config.ts
  14. 0 163
      public/app/core/directives/misc.js
  15. 226 0
      public/app/core/directives/misc.ts
  16. 46 53
      public/app/core/directives/ng_model_on_blur.ts
  17. 0 135
      public/app/core/directives/tags.js
  18. 131 0
      public/app/core/directives/tags.ts
  19. 3 3
      public/app/features/admin/admin.ts
  20. 7 11
      public/app/features/admin/admin_edit_org_ctrl.ts
  21. 8 10
      public/app/features/admin/admin_edit_user_ctrl.ts
  22. 7 11
      public/app/features/admin/admin_list_orgs_ctrl.ts
  23. 89 85
      public/app/features/dashlinks/module.ts
  24. 0 14
      public/app/features/org/all.js
  25. 10 0
      public/app/features/org/all.ts
  26. 8 13
      public/app/features/org/change_password_ctrl.ts
  27. 8 13
      public/app/features/org/new_org_ctrl.ts
  28. 8 10
      public/app/features/org/org_api_keys_ctrl.ts
  29. 7 10
      public/app/features/org/org_details_ctrl.ts
  30. 8 13
      public/app/features/org/select_org_ctrl.ts
  31. 9 11
      public/app/features/org/user_invite_ctrl.ts
  32. 0 56
      public/app/features/panellinks/module.js
  33. 57 0
      public/app/features/panellinks/module.ts
  34. 14 15
      public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts
  35. 2 0
      public/app/plugins/datasource/elasticsearch/config_ctrl.ts
  36. 11 5
      public/app/plugins/datasource/elasticsearch/datasource.ts
  37. 4 0
      public/app/plugins/datasource/elasticsearch/partials/config.html
  38. 0 981
      public/app/plugins/datasource/graphite/gfunc.js
  39. 611 442
      public/app/plugins/datasource/graphite/gfunc.ts
  40. 0 136
      public/app/plugins/datasource/prometheus/metric_find_query.js
  41. 147 0
      public/app/plugins/datasource/prometheus/metric_find_query.ts
  42. 24 25
      public/app/plugins/panel/graph/series_overrides_ctrl.ts
  43. 1 1
      tslint.json

+ 0 - 109
Dockerfile

@@ -1,109 +0,0 @@
-FROM phusion/baseimage:0.9.22
-MAINTAINER Denys Zhdanov <denis.zhdanov@gmail.com>
-
-RUN apt-get -y update \
-  && apt-get -y upgrade \
-  && apt-get -y install vim \
-  nginx \
-  python-dev \
-  python-flup \
-  python-pip \
-  python-ldap \
-  expect \
-  git \
-  memcached \
-  sqlite3 \
-  libffi-dev \
-  libcairo2 \
-  libcairo2-dev \
-  python-cairo \
-  python-rrdtool \
-  pkg-config \
-  nodejs \
-  && rm -rf /var/lib/apt/lists/*
-
-# choose a timezone at build-time
-# use `--build-arg CONTAINER_TIMEZONE=Europe/Brussels` in `docker build`
-ARG CONTAINER_TIMEZONE
-ENV DEBIAN_FRONTEND noninteractive
-
-RUN if [ ! -z "${CONTAINER_TIMEZONE}" ]; \
-    then ln -sf /usr/share/zoneinfo/$CONTAINER_TIMEZONE /etc/localtime && \
-    dpkg-reconfigure -f noninteractive tzdata; \
-    fi
-
-# fix python dependencies (LTS Django and newer memcached/txAMQP)
-RUN pip install --upgrade pip && \
-  pip install django==1.8.18 \
-  python-memcached==1.53 \
-  txAMQP==0.6.2
-
-ARG version=1.0.2
-ARG whisper_version=${version}
-ARG carbon_version=${version}
-ARG graphite_version=${version}
-
-ARG statsd_version=v0.7.2
-
-# install whisper
-RUN git clone -b ${whisper_version} --depth 1 https://github.com/graphite-project/whisper.git /usr/local/src/whisper
-WORKDIR /usr/local/src/whisper
-RUN python ./setup.py install
-
-# install carbon
-RUN git clone -b ${carbon_version} --depth 1 https://github.com/graphite-project/carbon.git /usr/local/src/carbon
-WORKDIR /usr/local/src/carbon
-RUN pip install -r requirements.txt \
-  && python ./setup.py install
-
-# install graphite
-RUN git clone -b ${graphite_version} --depth 1 https://github.com/graphite-project/graphite-web.git /usr/local/src/graphite-web
-WORKDIR /usr/local/src/graphite-web
-RUN pip install -r requirements.txt \
-  && python ./setup.py install
-ADD conf/opt/graphite/conf/*.conf /opt/graphite/conf/
-ADD conf/opt/graphite/webapp/graphite/local_settings.py /opt/graphite/webapp/graphite/local_settings.py
-# ADD conf/opt/graphite/webapp/graphite/app_settings.py /opt/graphite/webapp/graphite/app_settings.py
-WORKDIR /opt/graphite/webapp
-RUN mkdir -p /var/log/graphite/ \
-  && PYTHONPATH=/opt/graphite/webapp django-admin.py collectstatic --noinput --settings=graphite.settings
-
-# install statsd
-RUN git clone -b ${statsd_version} https://github.com/etsy/statsd.git /opt/statsd
-ADD conf/opt/statsd/config.js /opt/statsd/config.js
-
-# config nginx
-RUN rm /etc/nginx/sites-enabled/default
-ADD conf/etc/nginx/nginx.conf /etc/nginx/nginx.conf
-ADD conf/etc/nginx/sites-enabled/graphite-statsd.conf /etc/nginx/sites-enabled/graphite-statsd.conf
-
-# init django admin
-ADD conf/usr/local/bin/django_admin_init.exp /usr/local/bin/django_admin_init.exp
-ADD conf/usr/local/bin/manage.sh /usr/local/bin/manage.sh
-RUN chmod +x /usr/local/bin/manage.sh && /usr/local/bin/django_admin_init.exp
-
-# logging support
-RUN mkdir -p /var/log/carbon /var/log/graphite /var/log/nginx
-ADD conf/etc/logrotate.d/graphite-statsd /etc/logrotate.d/graphite-statsd
-
-# daemons
-ADD conf/etc/service/carbon/run /etc/service/carbon/run
-ADD conf/etc/service/carbon-aggregator/run /etc/service/carbon-aggregator/run
-ADD conf/etc/service/graphite/run /etc/service/graphite/run
-ADD conf/etc/service/statsd/run /etc/service/statsd/run
-ADD conf/etc/service/nginx/run /etc/service/nginx/run
-
-# default conf setup
-ADD conf /etc/graphite-statsd/conf
-ADD conf/etc/my_init.d/01_conf_init.sh /etc/my_init.d/01_conf_init.sh
-
-# cleanup
-RUN apt-get clean\
- && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
-
-# defaults
-EXPOSE 80 2003-2004 2023-2024 8125/udp 8126
-VOLUME ["/opt/graphite/conf", "/opt/graphite/storage", "/etc/nginx", "/opt/statsd", "/etc/logrotate.d", "/var/log"]
-WORKDIR /
-ENV HOME /root
-CMD ["/sbin/my_init"]

+ 0 - 22
LICENSE.txt

@@ -1,22 +0,0 @@
-Copyright (c) 2013-2016 Nathan Hopkins
-
-MIT License
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 1 - 0
conf/defaults.ini

@@ -495,3 +495,4 @@ public_url =
 [external_image_storage.gcs]
 key_file =
 bucket =
+path =

+ 1 - 0
conf/sample.ini

@@ -438,3 +438,4 @@ log_queries =
 [external_image_storage.gcs]
 ;key_file =
 ;bucket =
+;path =

+ 31 - 0
docs/sources/http_api/alerting.md

@@ -133,6 +133,37 @@ Content-Type: application/json
 }
 ```
 
+## Pause all alerts
+
+`POST /api/admin/pause-all-alerts`
+
+```http
+POST /api/admin/pause-all-alerts HTTP/1.1
+Accept: application/json
+Content-Type: application/json
+Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
+
+{
+  "paused": true
+}
+```
+
+JSON Body Schema:
+
+- **paused** – Can be `true` or `false`. True to pause an alert. False to unpause an alert.
+
+**Example Response**:
+
+```http
+HTTP/1.1 200
+Content-Type: application/json
+{
+  "state":   "Paused",
+  "message": "alert paused",
+  "alertsAffected": 1
+}
+```
+
 ## Get alert notifications
 
 `GET /api/alert-notifications`

+ 2 - 2
docs/sources/http_api/snapshot.md

@@ -140,9 +140,9 @@ Content-Type: application/json
 }
 ```
 
-## Delete Snapshot by Id
+## Delete Snapshot by deleteKey
 
-`GET /api/snapshots-delete/:key`
+`GET /api/snapshots-delete/:deleteKey`
 
 **Example Request**:
 

+ 3 - 0
docs/sources/installation/configuration.md

@@ -778,6 +778,9 @@ Service Account should have "Storage Object Writer" role.
 ### bucket name
 Bucket Name on Google Cloud Storage.
 
+### path
+Optional extra path inside bucket
+
 ## [alerting]
 
 ### enabled

+ 2 - 2
latest.json

@@ -1,4 +1,4 @@
 {
-  "stable": "4.5.2",
-	"testing": "4.5.2"
+  "stable": "4.6.2",
+	"testing": "4.6.2"
 }

+ 6 - 2
pkg/components/imguploader/gcsuploader.go

@@ -6,6 +6,7 @@ import (
 	"io/ioutil"
 	"net/http"
 	"os"
+	"path"
 
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/util"
@@ -20,19 +21,22 @@ const (
 type GCSUploader struct {
 	keyFile string
 	bucket  string
+	path    string
 	log     log.Logger
 }
 
-func NewGCSUploader(keyFile, bucket string) *GCSUploader {
+func NewGCSUploader(keyFile, bucket, path string) *GCSUploader {
 	return &GCSUploader{
 		keyFile: keyFile,
 		bucket:  bucket,
+		path:    path,
 		log:     log.New("gcsuploader"),
 	}
 }
 
 func (u *GCSUploader) Upload(ctx context.Context, imageDiskPath string) (string, error) {
-	key := util.GetRandomString(20) + ".png"
+	fileName := util.GetRandomString(20) + ".png"
+	key := path.Join(u.path, fileName)
 
 	u.log.Debug("Opening key file ", u.keyFile)
 	data, err := ioutil.ReadFile(u.keyFile)

+ 2 - 1
pkg/components/imguploader/imguploader.go

@@ -73,8 +73,9 @@ func NewImageUploader() (ImageUploader, error) {
 
 		keyFile := gcssec.Key("key_file").MustString("")
 		bucketName := gcssec.Key("bucket").MustString("")
+		path := gcssec.Key("path").MustString("")
 
-		return NewGCSUploader(keyFile, bucketName), nil
+		return NewGCSUploader(keyFile, bucketName, path), nil
 	}
 
 	return NopImageUploader{}, nil

+ 28 - 3
pkg/log/file.go

@@ -5,9 +5,10 @@
 package log
 
 import (
+	"bytes"
 	"errors"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"os"
 	"path/filepath"
 	"strings"
@@ -124,6 +125,30 @@ func (w *FileLogWriter) createLogFile() (*os.File, error) {
 	return os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
 }
 
+func (w *FileLogWriter) lineCounter() (int, error) {
+	r, err := os.OpenFile(w.Filename, os.O_RDONLY, 0644)
+	if err != nil {
+		return 0, fmt.Errorf("lineCounter Open File : %s", err)
+	}
+	buf := make([]byte, 32*1024)
+	count := 0
+
+	for {
+		c, err := r.Read(buf)
+		count += bytes.Count(buf[:c], []byte{'\n'})
+		switch {
+		case err == io.EOF:
+			if err := r.Close(); err != nil {
+				return count, err
+			}
+			return count, nil
+
+		case err != nil:
+			return count, err
+		}
+	}
+}
+
 func (w *FileLogWriter) initFd() error {
 	fd := w.mw.fd
 	finfo, err := fd.Stat()
@@ -133,11 +158,11 @@ func (w *FileLogWriter) initFd() error {
 	w.maxsize_cursize = int(finfo.Size())
 	w.daily_opendate = time.Now().Day()
 	if finfo.Size() > 0 {
-		content, err := ioutil.ReadFile(w.Filename)
+		count, err := w.lineCounter()
 		if err != nil {
 			return err
 		}
-		w.maxlines_curlines = len(strings.Split(string(content), "\n"))
+		w.maxlines_curlines = count
 	} else {
 		w.maxlines_curlines = 0
 	}

+ 44 - 0
pkg/log/file_test.go

@@ -0,0 +1,44 @@
+package log
+
+import (
+	"os"
+	"testing"
+
+	. "github.com/smartystreets/goconvey/convey"
+)
+
+func (w *FileLogWriter) WriteLine(line string) error {
+	n, err := w.mw.Write([]byte(line))
+	if err != nil {
+		return err
+	}
+	w.docheck(n)
+	return nil
+}
+
+func TestLogFile(t *testing.T) {
+
+	Convey("When logging to file", t, func() {
+		fileLogWrite := NewFileWriter()
+		So(fileLogWrite, ShouldNotBeNil)
+
+		fileLogWrite.Filename = "grafana_test.log"
+		err := fileLogWrite.Init()
+		So(err, ShouldBeNil)
+
+		Convey("Log file is empty", func() {
+			So(fileLogWrite.maxlines_curlines, ShouldEqual, 0)
+		})
+
+		Convey("Logging should add lines", func() {
+			err := fileLogWrite.WriteLine("test1\n")
+			err = fileLogWrite.WriteLine("test2\n")
+			err = fileLogWrite.WriteLine("test3\n")
+			So(err, ShouldBeNil)
+			So(fileLogWrite.maxlines_curlines, ShouldEqual, 3)
+		})
+
+		err = os.Remove(fileLogWrite.Filename)
+		So(err, ShouldBeNil)
+	})
+}

+ 2 - 0
public/app/core/config.ts

@@ -15,6 +15,8 @@ class Settings {
     disableLoginForm: boolean;
     defaultDatasource: string;
     alertingEnabled: boolean;
+    authProxyEnabled: boolean;
+    ldapEnabled: boolean;
 
     constructor(options) {
         var defaults = {

+ 0 - 163
public/app/core/directives/misc.js

@@ -1,163 +0,0 @@
-define([
-  'angular',
-  'require',
-  '../core_module',
-  'app/core/utils/kbn',
-],
-function (angular, require, coreModule, kbn) {
-  'use strict';
-
-  kbn = kbn.default;
-
-  coreModule.default.directive('tip', function($compile) {
-    return {
-      restrict: 'E',
-      link: function(scope, elem, attrs) {
-        var _t = '<i class="grafana-tip fa fa-'+(attrs.icon||'question-circle')+'" bs-tooltip="\''+
-          kbn.addslashes(elem.text())+'\'"></i>';
-        _t = _t.replace(/{/g, '\\{').replace(/}/g, '\\}');
-        elem.replaceWith($compile(angular.element(_t))(scope));
-      }
-    };
-  });
-
-  coreModule.default.directive('clipboardButton', function() {
-    return {
-      scope: {
-        getText: '&clipboardButton'
-      },
-      link: function(scope, elem) {
-        require(['clipboard'], function(Clipboard) {
-          scope.clipboard = new Clipboard(elem[0], {
-            text: function() {
-              return scope.getText();
-            }
-          });
-        });
-
-        scope.$on('$destroy', function() {
-          if (scope.clipboard) {
-            scope.clipboard.destroy();
-          }
-        });
-      }
-    };
-  });
-
-  coreModule.default.directive('compile', function($compile) {
-    return {
-      restrict: 'A',
-      link: function(scope, element, attrs) {
-        scope.$watch(function(scope) {
-          return scope.$eval(attrs.compile);
-        }, function(value) {
-          element.html(value);
-          $compile(element.contents())(scope);
-        });
-      }
-    };
-  });
-
-  coreModule.default.directive('watchChange', function() {
-    return {
-      scope: { onchange: '&watchChange' },
-      link: function(scope, element) {
-        element.on('input', function() {
-          scope.$apply(function () {
-            scope.onchange({ inputValue: element.val() });
-          });
-        });
-      }
-    };
-  });
-
-  coreModule.default.directive('editorOptBool', function($compile) {
-    return {
-      restrict: 'E',
-      link: function(scope, elem, attrs) {
-        var ngchange = attrs.change ? (' ng-change="' + attrs.change + '"') : '';
-        var tip = attrs.tip ? (' <tip>' + attrs.tip + '</tip>') : '';
-        var showIf = attrs.showIf ? (' ng-show="' + attrs.showIf + '" ') : '';
-
-        var template = '<div class="editor-option gf-form-checkbox text-center"' + showIf + '>' +
-          ' <label for="' + attrs.model + '" class="small">' +
-          attrs.text + tip + '</label>' +
-          '<input class="cr1" id="' + attrs.model + '" type="checkbox" ' +
-          '       ng-model="' + attrs.model + '"' + ngchange +
-          '       ng-checked="' + attrs.model + '"></input>' +
-          ' <label for="' + attrs.model + '" class="cr1"></label>';
-        elem.replaceWith($compile(angular.element(template))(scope));
-      }
-    };
-  });
-
-  coreModule.default.directive('editorCheckbox', function($compile, $interpolate) {
-    return {
-      restrict: 'E',
-      link: function(scope, elem, attrs) {
-        var text = $interpolate(attrs.text)(scope);
-        var model = $interpolate(attrs.model)(scope);
-        var ngchange = attrs.change ? (' ng-change="' + attrs.change + '"') : '';
-        var tip = attrs.tip ? (' <tip>' + attrs.tip + '</tip>') : '';
-        var label = '<label for="' + scope.$id + model + '" class="checkbox-label">' +
-          text + tip + '</label>';
-
-        var template =
-        '<input class="cr1" id="' + scope.$id + model + '" type="checkbox" ' +
-        '       ng-model="' + model + '"' + ngchange +
-        '       ng-checked="' + model + '"></input>' +
-        ' <label for="' + scope.$id + model + '" class="cr1"></label>';
-
-        template = template + label;
-        elem.addClass('gf-form-checkbox');
-        elem.html($compile(angular.element(template))(scope));
-      }
-    };
-  });
-
-  coreModule.default.directive('gfDropdown', function ($parse, $compile, $timeout) {
-    function buildTemplate(items, placement) {
-      var upclass = placement === 'top' ? 'dropup' : '';
-      var ul = [
-        '<ul class="dropdown-menu ' + upclass + '" role="menu" aria-labelledby="drop1">',
-        '</ul>'
-      ];
-
-      angular.forEach(items, function (item, index) {
-        if (item.divider) {
-          return ul.splice(index + 1, 0, '<li class="divider"></li>');
-        }
-
-        var li = '<li' + (item.submenu && item.submenu.length ? ' class="dropdown-submenu"' : '') + '>' +
-          '<a tabindex="-1" ng-href="' + (item.href || '') + '"' + (item.click ? ' ng-click="' + item.click + '"' : '') +
-          (item.target ? ' target="' + item.target + '"' : '') + (item.method ? ' data-method="' + item.method + '"' : '') +
-            '>' + (item.text || '') + '</a>';
-
-        if (item.submenu && item.submenu.length) {
-          li += buildTemplate(item.submenu).join('\n');
-        }
-
-        li += '</li>';
-        ul.splice(index + 1, 0, li);
-      });
-      return ul;
-    }
-
-    return {
-      restrict: 'EA',
-      scope: true,
-      link: function postLink(scope, iElement, iAttrs) {
-        var getter = $parse(iAttrs.gfDropdown), items = getter(scope);
-        $timeout(function () {
-          var placement = iElement.data('placement');
-          var dropdown = angular.element(buildTemplate(items, placement).join(''));
-          dropdown.insertAfter(iElement);
-          $compile(iElement.next('ul.dropdown-menu'))(scope);
-        });
-
-        iElement.addClass('dropdown-toggle').attr('data-toggle', 'dropdown');
-      }
-    };
-  });
-
-});

+ 226 - 0
public/app/core/directives/misc.ts

@@ -0,0 +1,226 @@
+import angular from "angular";
+import Clipboard from "clipboard";
+import coreModule from "../core_module";
+import kbn from "app/core/utils/kbn";
+
+/** @ngInject */
+function tip($compile) {
+  return {
+    restrict: "E",
+    link: function(scope, elem, attrs) {
+      var _t =
+        '<i class="grafana-tip fa fa-' +
+        (attrs.icon || "question-circle") +
+        '" bs-tooltip="\'' +
+        kbn.addslashes(elem.text()) +
+        "'\"></i>";
+      _t = _t.replace(/{/g, "\\{").replace(/}/g, "\\}");
+      elem.replaceWith($compile(angular.element(_t))(scope));
+    }
+  };
+}
+
+function clipboardButton() {
+  return {
+    scope: {
+      getText: "&clipboardButton"
+    },
+    link: function(scope, elem) {
+      scope.clipboard = new Clipboard(elem[0], {
+        text: function() {
+          return scope.getText();
+        }
+      });
+
+      scope.$on("$destroy", function() {
+        if (scope.clipboard) {
+          scope.clipboard.destroy();
+        }
+      });
+    }
+  };
+}
+
+/** @ngInject */
+function compile($compile) {
+  return {
+    restrict: "A",
+    link: function(scope, element, attrs) {
+      scope.$watch(
+        function(scope) {
+          return scope.$eval(attrs.compile);
+        },
+        function(value) {
+          element.html(value);
+          $compile(element.contents())(scope);
+        }
+      );
+    }
+  };
+}
+
+function watchChange() {
+  return {
+    scope: { onchange: "&watchChange" },
+    link: function(scope, element) {
+      element.on("input", function() {
+        scope.$apply(function() {
+          scope.onchange({ inputValue: element.val() });
+        });
+      });
+    }
+  };
+}
+
+/** @ngInject */
+function editorOptBool($compile) {
+  return {
+    restrict: "E",
+    link: function(scope, elem, attrs) {
+      var ngchange = attrs.change ? ' ng-change="' + attrs.change + '"' : "";
+      var tip = attrs.tip ? " <tip>" + attrs.tip + "</tip>" : "";
+      var showIf = attrs.showIf ? ' ng-show="' + attrs.showIf + '" ' : "";
+
+      var template =
+        '<div class="editor-option gf-form-checkbox text-center"' +
+        showIf +
+        ">" +
+        ' <label for="' +
+        attrs.model +
+        '" class="small">' +
+        attrs.text +
+        tip +
+        "</label>" +
+        '<input class="cr1" id="' +
+        attrs.model +
+        '" type="checkbox" ' +
+        '       ng-model="' +
+        attrs.model +
+        '"' +
+        ngchange +
+        '       ng-checked="' +
+        attrs.model +
+        '"></input>' +
+        ' <label for="' +
+        attrs.model +
+        '" class="cr1"></label>';
+      elem.replaceWith($compile(angular.element(template))(scope));
+    }
+  };
+}
+
+/** @ngInject */
+function editorCheckbox($compile, $interpolate) {
+  return {
+    restrict: "E",
+    link: function(scope, elem, attrs) {
+      var text = $interpolate(attrs.text)(scope);
+      var model = $interpolate(attrs.model)(scope);
+      var ngchange = attrs.change ? ' ng-change="' + attrs.change + '"' : "";
+      var tip = attrs.tip ? " <tip>" + attrs.tip + "</tip>" : "";
+      var label =
+        '<label for="' +
+        scope.$id +
+        model +
+        '" class="checkbox-label">' +
+        text +
+        tip +
+        "</label>";
+
+      var template =
+        '<input class="cr1" id="' +
+        scope.$id +
+        model +
+        '" type="checkbox" ' +
+        '       ng-model="' +
+        model +
+        '"' +
+        ngchange +
+        '       ng-checked="' +
+        model +
+        '"></input>' +
+        ' <label for="' +
+        scope.$id +
+        model +
+        '" class="cr1"></label>';
+
+      template = template + label;
+      elem.addClass("gf-form-checkbox");
+      elem.html($compile(angular.element(template))(scope));
+    }
+  };
+}
+
+/** @ngInject */
+function gfDropdown($parse, $compile, $timeout) {
+  function buildTemplate(items, placement?) {
+    var upclass = placement === "top" ? "dropup" : "";
+    var ul = [
+      '<ul class="dropdown-menu ' +
+        upclass +
+        '" role="menu" aria-labelledby="drop1">',
+      "</ul>"
+    ];
+
+    for (let index = 0; index < items.length; index++) {
+      let item = items[index];
+
+      if (item.divider) {
+        ul.splice(index + 1, 0, '<li class="divider"></li>');
+        continue;
+      }
+
+      var li =
+        "<li" +
+        (item.submenu && item.submenu.length
+          ? ' class="dropdown-submenu"'
+          : "") +
+        ">" +
+        '<a tabindex="-1" ng-href="' +
+        (item.href || "") +
+        '"' +
+        (item.click ? ' ng-click="' + item.click + '"' : "") +
+        (item.target ? ' target="' + item.target + '"' : "") +
+        (item.method ? ' data-method="' + item.method + '"' : "") +
+        ">" +
+        (item.text || "") +
+        "</a>";
+
+      if (item.submenu && item.submenu.length) {
+        li += buildTemplate(item.submenu).join("\n");
+      }
+
+      li += "</li>";
+      ul.splice(index + 1, 0, li);
+    }
+
+    return ul;
+  }
+
+  return {
+    restrict: "EA",
+    scope: true,
+    link: function postLink(scope, iElement, iAttrs) {
+      var getter = $parse(iAttrs.gfDropdown),
+        items = getter(scope);
+      $timeout(function() {
+        var placement = iElement.data("placement");
+        var dropdown = angular.element(
+          buildTemplate(items, placement).join("")
+        );
+        dropdown.insertAfter(iElement);
+        $compile(iElement.next("ul.dropdown-menu"))(scope);
+      });
+
+      iElement.addClass("dropdown-toggle").attr("data-toggle", "dropdown");
+    }
+  };
+}
+
+coreModule.directive("tip", tip);
+coreModule.directive("clipboardButton", clipboardButton);
+coreModule.directive("compile", compile);
+coreModule.directive("watchChange", watchChange);
+coreModule.directive("editorOptBool", editorOptBool);
+coreModule.directive("editorCheckbox", editorCheckbox);
+coreModule.directive("gfDropdown", gfDropdown);

+ 46 - 53
public/app/core/directives/ng_model_on_blur.ts

@@ -1,64 +1,57 @@
 import coreModule from '../core_module';
 import * as rangeUtil from 'app/core/utils/rangeutil';
 
-export class NgModelOnBlur {
-  constructor() {
-    return {
-      restrict: 'A',
-      priority: 1,
-      require: 'ngModel',
-      link: function(scope, elm, attr, ngModelCtrl) {
-        if (attr.type === 'radio' || attr.type === 'checkbox') {
-          return;
-        }
+function ngModelOnBlur() {
+  return {
+    restrict: 'A',
+    priority: 1,
+    require: 'ngModel',
+    link: function(scope, elm, attr, ngModelCtrl) {
+      if (attr.type === 'radio' || attr.type === 'checkbox') {
+        return;
+      }
 
-        elm.off('input keydown change');
-        elm.bind('blur', function() {
-          scope.$apply(function() {
-            ngModelCtrl.$setViewValue(elm.val());
-          });
+      elm.off('input keydown change');
+      elm.bind('blur', function() {
+        scope.$apply(function() {
+          ngModelCtrl.$setViewValue(elm.val());
         });
-      }
-    };
-  }
+      });
+    }
+  };
 }
 
-
-export class EmptyToNull {
-  constructor() {
-    return {
-      restrict: 'A',
-      require: 'ngModel',
-      link: function (scope, elm, attrs, ctrl) {
-        ctrl.$parsers.push(function (viewValue) {
-          if (viewValue === "") { return null; }
-          return viewValue;
-        });
-      }
-    };
-  }
+function emptyToNull() {
+  return {
+    restrict: 'A',
+    require: 'ngModel',
+    link: function (scope, elm, attrs, ctrl) {
+      ctrl.$parsers.push(function (viewValue) {
+        if (viewValue === "") { return null; }
+        return viewValue;
+      });
+    }
+  };
 }
 
-export class ValidTimeSpan {
-  constructor() {
-    return {
-      require: 'ngModel',
-      link: function(scope, elm, attrs, ctrl) {
-        ctrl.$validators.integer = function(modelValue, viewValue) {
-          if (ctrl.$isEmpty(modelValue)) {
-            return true;
-          }
-          if (viewValue.indexOf('$') === 0 || viewValue.indexOf('+$') === 0) {
-            return true; // allow template variable
-          }
-          var info = rangeUtil.describeTextRange(viewValue);
-          return info.invalid !== true;
-        };
-      }
-    };
-  }
+function validTimeSpan() {
+  return {
+    require: 'ngModel',
+    link: function(scope, elm, attrs, ctrl) {
+      ctrl.$validators.integer = function(modelValue, viewValue) {
+        if (ctrl.$isEmpty(modelValue)) {
+          return true;
+        }
+        if (viewValue.indexOf('$') === 0 || viewValue.indexOf('+$') === 0) {
+          return true; // allow template variable
+        }
+        var info = rangeUtil.describeTextRange(viewValue);
+        return info.invalid !== true;
+      };
+    }
+  };
 }
 
-coreModule.directive('ngModelOnblur', NgModelOnBlur);
-coreModule.directive('emptyToNull', EmptyToNull);
-coreModule.directive('validTimeSpan', ValidTimeSpan);
+coreModule.directive('ngModelOnblur', ngModelOnBlur);
+coreModule.directive('emptyToNull', emptyToNull);
+coreModule.directive('validTimeSpan', validTimeSpan);

+ 0 - 135
public/app/core/directives/tags.js

@@ -1,135 +0,0 @@
-define([
-  'angular',
-  'jquery',
-  '../core_module',
-  'vendor/tagsinput/bootstrap-tagsinput.js',
-],
-function (angular, $, coreModule) {
-  'use strict';
-
-  function djb2(str) {
-    var hash = 5381;
-    for (var i = 0; i < str.length; i++) {
-      hash = ((hash << 5) + hash) + str.charCodeAt(i); /* hash * 33 + c */
-    }
-    return hash;
-  }
-
-  function setColor(name, element) {
-    var hash = djb2(name.toLowerCase());
-    var colors = [
-      "#E24D42","#1F78C1","#BA43A9","#705DA0","#466803",
-      "#508642","#447EBC","#C15C17","#890F02","#757575",
-      "#0A437C","#6D1F62","#584477","#629E51","#2F4F4F",
-      "#BF1B00","#806EB7","#8a2eb8", "#699e00","#000000",
-      "#3F6833","#2F575E","#99440A","#E0752D","#0E4AB4",
-      "#58140C","#052B51","#511749","#3F2B5B",
-    ];
-    var borderColors = [
-      "#FF7368","#459EE7","#E069CF","#9683C6","#6C8E29",
-      "#76AC68","#6AA4E2","#E7823D","#AF3528","#9B9B9B",
-      "#3069A2","#934588","#7E6A9D","#88C477","#557575",
-      "#E54126","#A694DD","#B054DE", "#8FC426","#262626",
-      "#658E59","#557D84","#BF6A30","#FF9B53","#3470DA",
-      "#7E3A32","#2B5177","#773D6F","#655181",
-    ];
-    var color = colors[Math.abs(hash % colors.length)];
-    var borderColor = borderColors[Math.abs(hash % borderColors.length)];
-    element.css("background-color", color);
-    element.css("border-color", borderColor);
-  }
-
-  coreModule.default.directive('tagColorFromName', function() {
-    return {
-      scope: { tagColorFromName: "=" },
-      link: function (scope, element) {
-        setColor(scope.tagColorFromName, element);
-      }
-    };
-  });
-
-  coreModule.default.directive('bootstrapTagsinput', function() {
-
-    function getItemProperty(scope, property) {
-      if (!property) {
-        return undefined;
-      }
-
-      if (angular.isFunction(scope.$parent[property])) {
-        return scope.$parent[property];
-      }
-
-      return function(item) {
-        return item[property];
-      };
-    }
-
-    return {
-      restrict: 'EA',
-      scope: {
-        model: '=ngModel',
-        onTagsUpdated: "&",
-      },
-      template: '<select multiple></select>',
-      replace: false,
-      link: function(scope, element, attrs) {
-
-        if (!angular.isArray(scope.model)) {
-          scope.model = [];
-        }
-
-        var select = $('select', element);
-
-        if (attrs.placeholder) {
-          select.attr('placeholder', attrs.placeholder);
-        }
-
-        select.tagsinput({
-          typeahead: {
-            source: angular.isFunction(scope.$parent[attrs.typeaheadSource]) ? scope.$parent[attrs.typeaheadSource] : null
-          },
-          widthClass: attrs.widthClass,
-          itemValue: getItemProperty(scope, attrs.itemvalue),
-          itemText : getItemProperty(scope, attrs.itemtext),
-          tagClass : angular.isFunction(scope.$parent[attrs.tagclass]) ?
-            scope.$parent[attrs.tagclass] : function() { return attrs.tagclass; }
-        });
-
-        select.on('itemAdded', function(event) {
-          if (scope.model.indexOf(event.item) === -1) {
-            scope.model.push(event.item);
-            if (scope.onTagsUpdated) {
-              scope.onTagsUpdated();
-            }
-          }
-          var tagElement = select.next().children("span").filter(function() { return $(this).text() === event.item; });
-          setColor(event.item, tagElement);
-        });
-
-        select.on('itemRemoved', function(event) {
-          var idx = scope.model.indexOf(event.item);
-          if (idx !== -1) {
-            scope.model.splice(idx, 1);
-            if (scope.onTagsUpdated) {
-              scope.onTagsUpdated();
-            }
-          }
-        });
-
-        scope.$watch("model", function() {
-          if (!angular.isArray(scope.model)) {
-            scope.model = [];
-          }
-
-          select.tagsinput('removeAll');
-
-          for (var i = 0; i < scope.model.length; i++) {
-            select.tagsinput('add', scope.model[i]);
-          }
-
-        }, true);
-      }
-    };
-  });
-
-});

+ 131 - 0
public/app/core/directives/tags.ts

@@ -0,0 +1,131 @@
+import angular from 'angular';
+import $ from 'jquery';
+import coreModule from '../core_module';
+import 'vendor/tagsinput/bootstrap-tagsinput.js';
+
+function djb2(str) {
+  var hash = 5381;
+  for (var i = 0; i < str.length; i++) {
+    hash = ((hash << 5) + hash) + str.charCodeAt(i); /* hash * 33 + c */
+  }
+  return hash;
+}
+
+function setColor(name, element) {
+  var hash = djb2(name.toLowerCase());
+  var colors = [
+    "#E24D42","#1F78C1","#BA43A9","#705DA0","#466803",
+    "#508642","#447EBC","#C15C17","#890F02","#757575",
+    "#0A437C","#6D1F62","#584477","#629E51","#2F4F4F",
+    "#BF1B00","#806EB7","#8a2eb8", "#699e00","#000000",
+    "#3F6833","#2F575E","#99440A","#E0752D","#0E4AB4",
+    "#58140C","#052B51","#511749","#3F2B5B",
+  ];
+  var borderColors = [
+    "#FF7368","#459EE7","#E069CF","#9683C6","#6C8E29",
+    "#76AC68","#6AA4E2","#E7823D","#AF3528","#9B9B9B",
+    "#3069A2","#934588","#7E6A9D","#88C477","#557575",
+    "#E54126","#A694DD","#B054DE", "#8FC426","#262626",
+    "#658E59","#557D84","#BF6A30","#FF9B53","#3470DA",
+    "#7E3A32","#2B5177","#773D6F","#655181",
+  ];
+  var color = colors[Math.abs(hash % colors.length)];
+  var borderColor = borderColors[Math.abs(hash % borderColors.length)];
+  element.css("background-color", color);
+  element.css("border-color", borderColor);
+}
+
+function tagColorFromName() {
+  return {
+    scope: { tagColorFromName: "=" },
+    link: function (scope, element) {
+      setColor(scope.tagColorFromName, element);
+    }
+  };
+}
+
+function bootstrapTagsinput() {
+  function getItemProperty(scope, property) {
+    if (!property) {
+      return undefined;
+    }
+
+    if (angular.isFunction(scope.$parent[property])) {
+      return scope.$parent[property];
+    }
+
+    return function(item) {
+      return item[property];
+    };
+  }
+
+  return {
+    restrict: 'EA',
+    scope: {
+      model: '=ngModel',
+      onTagsUpdated: "&",
+    },
+    template: '<select multiple></select>',
+    replace: false,
+    link: function(scope, element, attrs) {
+
+      if (!angular.isArray(scope.model)) {
+        scope.model = [];
+      }
+
+      var select = $('select', element);
+
+      if (attrs.placeholder) {
+        select.attr('placeholder', attrs.placeholder);
+      }
+
+      select.tagsinput({
+        typeahead: {
+          source: angular.isFunction(scope.$parent[attrs.typeaheadSource]) ? scope.$parent[attrs.typeaheadSource] : null
+        },
+        widthClass: attrs.widthClass,
+        itemValue: getItemProperty(scope, attrs.itemvalue),
+        itemText : getItemProperty(scope, attrs.itemtext),
+        tagClass : angular.isFunction(scope.$parent[attrs.tagclass]) ?
+          scope.$parent[attrs.tagclass] : function() { return attrs.tagclass; }
+      });
+
+      select.on('itemAdded', function(event) {
+        if (scope.model.indexOf(event.item) === -1) {
+          scope.model.push(event.item);
+          if (scope.onTagsUpdated) {
+            scope.onTagsUpdated();
+          }
+        }
+        var tagElement = select.next().children("span").filter(function() { return $(this).text() === event.item; });
+        setColor(event.item, tagElement);
+      });
+
+      select.on('itemRemoved', function(event) {
+        var idx = scope.model.indexOf(event.item);
+        if (idx !== -1) {
+          scope.model.splice(idx, 1);
+          if (scope.onTagsUpdated) {
+            scope.onTagsUpdated();
+          }
+        }
+      });
+
+      scope.$watch("model", function() {
+        if (!angular.isArray(scope.model)) {
+          scope.model = [];
+        }
+
+        select.tagsinput('removeAll');
+
+        for (var i = 0; i < scope.model.length; i++) {
+          select.tagsinput('add', scope.model[i]);
+        }
+
+      }, true);
+    }
+  };
+}
+
+coreModule.directive('tagColorFromName', tagColorFromName);
+coreModule.directive('bootstrapTagsinput', bootstrapTagsinput);

+ 3 - 3
public/app/features/admin/admin.ts

@@ -1,7 +1,7 @@
 import  AdminListUsersCtrl from './admin_list_users_ctrl';
-import  './adminListOrgsCtrl';
-import  './adminEditOrgCtrl';
-import  './adminEditUserCtrl';
+import  './admin_list_orgs_ctrl';
+import  './admin_edit_org_ctrl';
+import  './admin_edit_user_ctrl';
 
 import coreModule from 'app/core/core_module';
 

+ 7 - 11
public/app/features/admin/adminEditOrgCtrl.js → public/app/features/admin/admin_edit_org_ctrl.ts

@@ -1,13 +1,9 @@
-define([
-  'angular',
-],
-function (angular) {
-  'use strict';
+import angular from 'angular';
 
-  var module = angular.module('grafana.controllers');
-
-  module.controller('AdminEditOrgCtrl', function($scope, $routeParams, backendSrv, $location, navModelSrv) {
+export class AdminEditOrgCtrl {
 
+  /** @ngInject */
+  constructor($scope, $routeParams, backendSrv, $location, navModelSrv) {
     $scope.init = function() {
       $scope.navModel = navModelSrv.getNav('cfg', 'admin', 'global-orgs');
 
@@ -48,7 +44,7 @@ function (angular) {
     };
 
     $scope.init();
+  }
+}
 
-  });
-
-});
+angular.module('grafana.controllers').controller('AdminEditOrgCtrl', AdminEditOrgCtrl);

+ 8 - 10
public/app/features/admin/adminEditUserCtrl.js → public/app/features/admin/admin_edit_user_ctrl.ts

@@ -1,13 +1,10 @@
-define([
-  'angular',
-  'lodash',
-],
-function (angular, _) {
-  'use strict';
+import angular from 'angular';
+import _ from 'lodash';
 
-  var module = angular.module('grafana.controllers');
+export class AdminEditUserCtrl {
 
-  module.controller('AdminEditUserCtrl', function($scope, $routeParams, backendSrv, $location, navModelSrv) {
+  /** @ngInject */
+  constructor($scope, $routeParams, backendSrv, $location, navModelSrv) {
     $scope.user = {};
     $scope.newOrg = { name: '', role: 'Editor' };
     $scope.permissions = {};
@@ -106,6 +103,7 @@ function (angular, _) {
     };
 
     $scope.init();
+  }
+}
 
-  });
-});
+angular.module('grafana.controllers').controller('AdminEditUserCtrl', AdminEditUserCtrl);

+ 7 - 11
public/app/features/admin/adminListOrgsCtrl.js → public/app/features/admin/admin_list_orgs_ctrl.ts

@@ -1,13 +1,9 @@
-define([
-  'angular',
-],
-function (angular) {
-  'use strict';
+import angular from 'angular';
 
-  var module = angular.module('grafana.controllers');
-
-  module.controller('AdminListOrgsCtrl', function($scope, backendSrv, navModelSrv) {
+export class AdminListOrgsCtrl {
 
+  /** @ngInject */
+  constructor($scope, backendSrv, navModelSrv) {
     $scope.init = function() {
       $scope.navModel = navModelSrv.getNav('cfg', 'admin', 'global-orgs');
       $scope.getOrgs();
@@ -35,7 +31,7 @@ function (angular) {
     };
 
     $scope.init();
+  }
+}
 
-  });
-
-});
+angular.module('grafana.controllers').controller('AdminListOrgsCtrl', AdminListOrgsCtrl);

+ 89 - 85
public/app/features/dashlinks/module.js → public/app/features/dashlinks/module.ts

@@ -1,92 +1,89 @@
-define([
-  'angular',
-  'lodash',
-],
-function (angular, _) {
-  'use strict';
-
-  var module = angular.module('grafana.directives');
-
-  var iconMap = {
-    "external link": "fa-external-link",
-    "dashboard": "fa-th-large",
-    "question": "fa-question",
-    "info": "fa-info",
-    "bolt": "fa-bolt",
-    "doc": "fa-file-text-o",
-    "cloud": "fa-cloud",
+import angular from 'angular';
+import _ from 'lodash';
+
+var iconMap = {
+  "external link": "fa-external-link",
+  "dashboard": "fa-th-large",
+  "question": "fa-question",
+  "info": "fa-info",
+  "bolt": "fa-bolt",
+  "doc": "fa-file-text-o",
+  "cloud": "fa-cloud",
+};
+
+function dashLinksEditor() {
+  return {
+    restrict: 'E',
+    controller: 'DashLinkEditorCtrl',
+    templateUrl: 'public/app/features/dashlinks/editor.html',
+    link: function() {
+    }
   };
-
-  module.directive('dashLinksEditor', function() {
-    return {
-      restrict: 'E',
-      controller: 'DashLinkEditorCtrl',
-      templateUrl: 'public/app/features/dashlinks/editor.html',
-      link: function() {
+}
+
+function dashLinksContainer() {
+  return {
+    scope: {
+      links: "="
+    },
+    restrict: 'E',
+    controller: 'DashLinksContainerCtrl',
+    template: '<dash-link ng-repeat="link in generatedLinks" link="link"></dash-link>',
+    link: function() { }
+  };
+}
+
+/** @ngInject */
+function dashLink($compile, linkSrv) {
+  return {
+    restrict: 'E',
+    link: function(scope, elem) {
+      var link = scope.link;
+      var template = '<div class="gf-form">' +
+        '<a class="pointer gf-form-label" data-placement="bottom"' +
+        (link.asDropdown ? ' ng-click="fillDropdown(link)" data-toggle="dropdown"'  : "") + '>' +
+        '<i></i> <span></span></a>';
+
+      if (link.asDropdown) {
+        template += '<ul class="dropdown-menu" role="menu">' +
+          '<li ng-repeat="dash in link.searchHits"><a href="{{dash.url}}">{{dash.title}}</a></li>' +
+          '</ul>';
       }
-    };
-  });
-
-  module.directive('dashLinksContainer', function() {
-    return {
-      scope: {
-        links: "="
-      },
-      restrict: 'E',
-      controller: 'DashLinksContainerCtrl',
-      template: '<dash-link ng-repeat="link in generatedLinks" link="link"></dash-link>',
-      link: function() { }
-    };
-  });
-
-  module.directive('dashLink', function($compile, linkSrv) {
-    return {
-      restrict: 'E',
-      link: function(scope, elem) {
-        var link = scope.link;
-        var template = '<div class="gf-form">' +
-          '<a class="pointer gf-form-label" data-placement="bottom"' +
-          (link.asDropdown ? ' ng-click="fillDropdown(link)" data-toggle="dropdown"'  : "") + '>' +
-          '<i></i> <span></span></a>';
-
-        if (link.asDropdown) {
-          template += '<ul class="dropdown-menu" role="menu">' +
-            '<li ng-repeat="dash in link.searchHits"><a href="{{dash.url}}">{{dash.title}}</a></li>' +
-            '</ul>';
-        }
 
-        template += '</div>';
+      template += '</div>';
 
-        elem.html(template);
-        $compile(elem.contents())(scope);
+      elem.html(template);
+      $compile(elem.contents())(scope);
 
-        var anchor = elem.find('a');
-        var icon = elem.find('i');
-        var span = elem.find('span');
-
-        function update() {
-          var linkInfo = linkSrv.getAnchorInfo(link);
-          span.text(linkInfo.title);
-          anchor.attr("href", linkInfo.href);
-        }
+      var anchor = elem.find('a');
+      var icon = elem.find('i');
+      var span = elem.find('span');
 
-        // tooltip
-        elem.find('a').tooltip({ title: scope.link.tooltip, html: true, container: 'body' });
-        icon.attr('class', 'fa fa-fw ' + scope.link.icon);
-        anchor.attr('target', scope.link.target);
+      function update() {
+        var linkInfo = linkSrv.getAnchorInfo(link);
+        span.text(linkInfo.title);
+        anchor.attr("href", linkInfo.href);
+      }
 
-        // fix for menus on the far right
-        if (link.asDropdown && scope.$last) {
-          elem.find('.dropdown-menu').addClass('pull-right');
-        }
+      // tooltip
+      elem.find('a').tooltip({ title: scope.link.tooltip, html: true, container: 'body' });
+      icon.attr('class', 'fa fa-fw ' + scope.link.icon);
+      anchor.attr('target', scope.link.target);
 
-        update();
-        scope.$on('refresh', update);
+      // fix for menus on the far right
+      if (link.asDropdown && scope.$last) {
+        elem.find('.dropdown-menu').addClass('pull-right');
       }
-    };
-  });
 
-  module.controller("DashLinksContainerCtrl", function($scope, $rootScope, $q, backendSrv, dashboardSrv, linkSrv) {
+      update();
+      scope.$on('refresh', update);
+    }
+  };
+}
+
+export class DashLinksContainerCtrl {
+  /** @ngInject */
+  constructor($scope, $rootScope, $q, backendSrv, dashboardSrv, linkSrv) {
     var currentDashId = dashboardSrv.getCurrent().id;
 
     function buildLinks(linkDef) {
@@ -162,10 +159,12 @@ function (angular, _) {
 
     updateDashLinks();
     $rootScope.onAppEvent('dash-links-updated', updateDashLinks, $scope);
-  });
-
-  module.controller('DashLinkEditorCtrl', function($scope, $rootScope) {
+  }
+}
 
+export class DashLinkEditorCtrl {
+  /** @ngInject */
+  constructor($scope, $rootScope) {
     $scope.iconMap = iconMap;
     $scope.dashboard.links = $scope.dashboard.links || [];
 
@@ -189,6 +188,11 @@ function (angular, _) {
       $scope.dashboard.updateSubmenuVisibility();
       $scope.updated();
     };
-
-  });
-});
+  }
+}
+
+angular.module('grafana.directives').directive('dashLinksEditor', dashLinksEditor);
+angular.module('grafana.directives').directive('dashLinksContainer', dashLinksContainer);
+angular.module('grafana.directives').directive('dashLink', dashLink);
+angular.module('grafana.directives').controller("DashLinksContainerCtrl", DashLinksContainerCtrl);
+angular.module('grafana.directives').controller('DashLinkEditorCtrl', DashLinkEditorCtrl);

+ 0 - 14
public/app/features/org/all.js

@@ -1,14 +0,0 @@
-define([
-  './org_users_ctrl',
-  './profile_ctrl',
-  './select_org_ctrl',
-  './change_password_ctrl',
-  './newOrgCtrl',
-  './userInviteCtrl',
-  './orgApiKeysCtrl',
-  './orgDetailsCtrl',
-  './prefs_control',
-  './user_groups_ctrl',
-  './user_group_details_ctrl',
-  './create_user_group_modal',
-], function () {});

+ 10 - 0
public/app/features/org/all.ts

@@ -0,0 +1,10 @@
+import './org_users_ctrl';
+import './profile_ctrl';
+import './org_users_ctrl';
+import './select_org_ctrl';
+import './change_password_ctrl';
+import './new_org_ctrl';
+import './user_invite_ctrl';
+import './org_api_keys_ctrl';
+import './org_details_ctrl';
+import './prefs_control';

+ 8 - 13
public/app/features/org/change_password_ctrl.js → public/app/features/org/change_password_ctrl.ts

@@ -1,16 +1,10 @@
-define([
-  'angular',
-  'app/core/config',
-],
-function (angular, config) {
-  'use strict';
+import angular from 'angular';
+import config from 'app/core/config';
 
-  config = config.default;
-
-  var module = angular.module('grafana.controllers');
-
-  module.controller('ChangePasswordCtrl', function($scope, backendSrv, $location, navModelSrv) {
+export class ChangePasswordCtrl {
 
+  /** @ngInject **/
+  constructor($scope, backendSrv, $location, navModelSrv) {
     $scope.command = {};
     $scope.authProxyEnabled = config.authProxyEnabled;
     $scope.ldapEnabled = config.ldapEnabled;
@@ -28,6 +22,7 @@ function (angular, config) {
         $location.path("profile");
       });
     };
+  }
+}
 
-  });
-});
+angular.module('grafana.controllers').controller('ChangePasswordCtrl', ChangePasswordCtrl);

+ 8 - 13
public/app/features/org/newOrgCtrl.js → public/app/features/org/new_org_ctrl.ts

@@ -1,16 +1,10 @@
-define([
-  'angular',
-  'app/core/config',
-],
-function (angular, config) {
-  'use strict';
+import angular from 'angular';
+import config from 'app/core/config';
 
-  config = config.default;
-
-  var module = angular.module('grafana.controllers');
-
-  module.controller('NewOrgCtrl', function($scope, $http, backendSrv, navModelSrv) {
+export class NewOrgCtrl {
 
+  /** @ngInject **/
+  constructor($scope, $http, backendSrv, navModelSrv) {
     $scope.navModel = navModelSrv.getOrgNav(0);
     $scope.newOrg = {name: ''};
 
@@ -21,6 +15,7 @@ function (angular, config) {
         });
       });
     };
+  }
+}
 
-  });
-});
+angular.module('grafana.controllers').controller('NewOrgCtrl', NewOrgCtrl);

+ 8 - 10
public/app/features/org/orgApiKeysCtrl.js → public/app/features/org/org_api_keys_ctrl.ts

@@ -1,14 +1,11 @@
-define([
-  'angular',
-],
-function (angular) {
-  'use strict';
+import angular from 'angular';
 
-  var module = angular.module('grafana.controllers');
-
-  module.controller('OrgApiKeysCtrl', function($scope, $http, backendSrv, navModelSrv) {
+export class OrgApiKeysCtrl {
 
+  /** @ngInject **/
+  constructor ($scope, $http, backendSrv, navModelSrv) {
     $scope.navModel = navModelSrv.getNav('cfg', 'apikeys');
+
     $scope.roleTypes = ['Viewer', 'Editor', 'Admin'];
     $scope.token = { role: 'Viewer' };
 
@@ -43,6 +40,7 @@ function (angular) {
     };
 
     $scope.init();
+  }
+}
 
-  });
-});
+angular.module('grafana.controllers').controller('OrgApiKeysCtrl', OrgApiKeysCtrl);

+ 7 - 10
public/app/features/org/orgDetailsCtrl.js → public/app/features/org/org_details_ctrl.ts

@@ -1,13 +1,9 @@
-define([
-  'angular',
-],
-function (angular) {
-  'use strict';
+import angular from 'angular';
 
-  var module = angular.module('grafana.controllers');
-
-  module.controller('OrgDetailsCtrl', function($scope, $http, backendSrv, contextSrv, navModelSrv) {
+export class OrgDetailsCtrl {
 
+  /** @ngInject **/
+  constructor($scope, $http, backendSrv, contextSrv, navModelSrv) {
     $scope.init = function() {
       $scope.getOrgInfo();
       $scope.navModel = navModelSrv.getNav('cfg', 'org');
@@ -33,6 +29,7 @@ function (angular) {
     };
 
     $scope.init();
+  }
+}
 
-  });
-});
+angular.module('grafana.controllers').controller('OrgDetailsCtrl', OrgDetailsCtrl);

+ 8 - 13
public/app/features/org/select_org_ctrl.js → public/app/features/org/select_org_ctrl.ts

@@ -1,16 +1,10 @@
-define([
-  'angular',
-  'app/core/config',
-],
-function (angular, config) {
-  'use strict';
+import angular from 'angular';
+import config from 'app/core/config';
 
-  config = config.default;
-
-  var module = angular.module('grafana.controllers');
-
-  module.controller('SelectOrgCtrl', function($scope, backendSrv, contextSrv) {
+export class SelectOrgCtrl {
 
+  /** @ngInject **/
+  constructor($scope, backendSrv, contextSrv) {
     contextSrv.sidemenu = false;
 
     $scope.init = function() {
@@ -30,6 +24,7 @@ function (angular, config) {
     };
 
     $scope.init();
+  }
+}
 
-  });
-});
+angular.module('grafana.controllers').controller('SelectOrgCtrl', SelectOrgCtrl);

+ 9 - 11
public/app/features/org/userInviteCtrl.js → public/app/features/org/user_invite_ctrl.ts

@@ -1,14 +1,10 @@
-define([
-  'angular',
-  'lodash',
-],
-function (angular, _) {
-  'use strict';
+import angular from 'angular';
+import _ from 'lodash';
 
-  var module = angular.module('grafana.controllers');
-
-  module.controller('UserInviteCtrl', function($scope, backendSrv) {
+export class UserInviteCtrl {
 
+  /** @ngInject **/
+  constructor($scope, backendSrv) {
     $scope.invites = [
       {name: '', email: '', role: 'Editor'},
     ];
@@ -44,5 +40,7 @@ function (angular, _) {
         }
       });
     };
-  });
-});
+  }
+}
+
+angular.module('grafana.controllers').controller('UserInviteCtrl', UserInviteCtrl);

+ 0 - 56
public/app/features/panellinks/module.js

@@ -1,56 +0,0 @@
-define([
-  'angular',
-  'lodash',
-  './link_srv',
-],
-function (angular, _) {
-  'use strict';
-
-  angular
-    .module('grafana.directives')
-    .directive('panelLinksEditor', function() {
-      return {
-        scope: {
-          panel: "="
-        },
-        restrict: 'E',
-        controller: 'PanelLinksEditorCtrl',
-        templateUrl: 'public/app/features/panellinks/module.html',
-        link: function() {
-        }
-      };
-    }).controller('PanelLinksEditorCtrl', function($scope, backendSrv) {
-
-      $scope.panel.links = $scope.panel.links || [];
-
-      $scope.addLink = function() {
-        $scope.panel.links.push({
-          type: 'dashboard',
-        });
-      };
-
-      $scope.searchDashboards = function(queryStr, callback) {
-        backendSrv.search({query: queryStr}).then(function(hits) {
-          var dashboards = _.map(hits, function(dash) {
-            return dash.title;
-          });
-
-          callback(dashboards);
-        });
-      };
-
-      $scope.dashboardChanged = function(link) {
-        backendSrv.search({query: link.dashboard}).then(function(hits) {
-          var dashboard = _.find(hits, {title: link.dashboard});
-          if (dashboard) {
-            link.dashUri = dashboard.uri;
-            link.title = dashboard.title;
-          }
-        });
-      };
-
-      $scope.deleteLink = function(link) {
-        $scope.panel.links = _.without($scope.panel.links, link);
-      };
-    });
-});

+ 57 - 0
public/app/features/panellinks/module.ts

@@ -0,0 +1,57 @@
+import angular from 'angular';
+import _ from 'lodash';
+import './link_srv';
+
+function panelLinksEditor() {
+  return {
+    scope: {
+      panel: "="
+    },
+    restrict: 'E',
+    controller: 'PanelLinksEditorCtrl',
+    templateUrl: 'public/app/features/panellinks/module.html',
+    link: function() {
+    }
+  };
+}
+
+export class PanelLinksEditorCtrl {
+  /** @ngInject */
+  constructor($scope, backendSrv) {
+    $scope.panel.links = $scope.panel.links || [];
+
+    $scope.addLink = function() {
+      $scope.panel.links.push({
+        type: 'dashboard',
+      });
+    };
+
+    $scope.searchDashboards = function(queryStr, callback) {
+      backendSrv.search({query: queryStr}).then(function(hits) {
+        var dashboards = _.map(hits, function(dash) {
+          return dash.title;
+        });
+
+        callback(dashboards);
+      });
+    };
+
+    $scope.dashboardChanged = function(link) {
+      backendSrv.search({query: link.dashboard}).then(function(hits) {
+        var dashboard = _.find(hits, {title: link.dashboard});
+        if (dashboard) {
+          link.dashUri = dashboard.uri;
+          link.title = dashboard.title;
+        }
+      });
+    };
+
+    $scope.deleteLink = function(link) {
+      $scope.panel.links = _.without($scope.panel.links, link);
+    };
+  }
+}
+
+angular.module('grafana.directives').directive('panelLinksEditor', panelLinksEditor)
+.controller('PanelLinksEditorCtrl', PanelLinksEditorCtrl);
+

+ 14 - 15
public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.js → public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts

@@ -1,13 +1,9 @@
-define([
-  'angular',
-  'lodash',
-],
-function (angular, _) {
-  'use strict';
+import angular from 'angular';
+import _ from 'lodash';
 
-  var module = angular.module('grafana.controllers');
+export class CloudWatchQueryParameter {
 
-  module.directive('cloudwatchQueryParameter', function() {
+  constructor() {
     return {
       templateUrl: 'public/app/plugins/datasource/cloudwatch/partials/query.parameter.html',
       controller: 'CloudWatchQueryParameterCtrl',
@@ -18,9 +14,12 @@ function (angular, _) {
         onChange: "&",
       }
     };
-  });
+  }
+}
 
-  module.controller('CloudWatchQueryParameterCtrl', function($scope, templateSrv, uiSegmentSrv, datasourceSrv, $q) {
+export class CloudWatchQueryParameterCtrl {
+
+  constructor($scope, templateSrv, uiSegmentSrv, datasourceSrv, $q) {
 
     $scope.init = function() {
       var target = $scope.target;
@@ -120,8 +119,7 @@ function (angular, _) {
 
       if (segment.value === $scope.removeDimSegment.value) {
         $scope.dimSegments.splice(index, 3);
-      }
-      else if (segment.type === 'plus-button') {
+      } else if (segment.type === 'plus-button') {
         $scope.dimSegments.push(uiSegmentSrv.newOperator('='));
         $scope.dimSegments.push(uiSegmentSrv.newFake('select dimension value', 'value', 'query-segment-value'));
         segment.type = 'key';
@@ -195,7 +193,8 @@ function (angular, _) {
     };
 
     $scope.init();
+  }
+}
 
-  });
-
-});
+angular.module('grafana.controllers').directive('cloudwatchQueryParameter', CloudWatchQueryParameter);
+angular.module('grafana.controllers').controller('CloudWatchQueryParameterCtrl', CloudWatchQueryParameterCtrl);

+ 2 - 0
public/app/plugins/datasource/elasticsearch/config_ctrl.ts

@@ -8,6 +8,7 @@ export class ElasticConfigCtrl {
   constructor($scope) {
     this.current.jsonData.timeField = this.current.jsonData.timeField || '@timestamp';
     this.current.jsonData.esVersion = this.current.jsonData.esVersion || 5;
+    this.current.jsonData.maxConcurrentShardRequests = this.current.jsonData.maxConcurrentShardRequests || 256;
   }
 
   indexPatternTypes = [
@@ -22,6 +23,7 @@ export class ElasticConfigCtrl {
   esVersions = [
     {name: '2.x', value: 2},
     {name: '5.x', value: 5},
+    {name: '5.6+', value: 56},
   ];
 
   indexPatternTypeChanged() {

+ 11 - 5
public/app/plugins/datasource/elasticsearch/datasource.ts

@@ -16,6 +16,7 @@ export class ElasticDatasource {
   timeField: string;
   esVersion: number;
   interval: string;
+  maxConcurrentShardRequests: number;
   queryBuilder: ElasticQueryBuilder;
   indexPattern: IndexPattern;
 
@@ -30,6 +31,7 @@ export class ElasticDatasource {
     this.esVersion = instanceSettings.jsonData.esVersion;
     this.indexPattern = new IndexPattern(instanceSettings.index, instanceSettings.jsonData.interval);
     this.interval = instanceSettings.jsonData.timeInterval;
+    this.maxConcurrentShardRequests = instanceSettings.jsonData.maxConcurrentShardRequests;
     this.queryBuilder = new ElasticQueryBuilder({
       timeField: this.timeField,
       esVersion: this.esVersion,
@@ -213,11 +215,15 @@ export class ElasticDatasource {
   }
 
   getQueryHeader(searchType, timeFrom, timeTo) {
-    return angular.toJson({
-        search_type: searchType,
-        "ignore_unavailable": true,
-        index: this.indexPattern.getIndexList(timeFrom, timeTo),
-    });
+    var query_header: any = {
+            search_type: searchType,
+            "ignore_unavailable": true,
+            index: this.indexPattern.getIndexList(timeFrom, timeTo),
+        };
+    if (this.esVersion >= 56) {
+        query_header["max_concurrent_shard_requests"] = this.maxConcurrentShardRequests;
+    }
+    return angular.toJson(query_header);
   }
 
   query(options) {

+ 4 - 0
public/app/plugins/datasource/elasticsearch/partials/config.html

@@ -25,6 +25,10 @@
 		<span class="gf-form-label width-9">Version</span>
 		<select class="gf-form-input gf-size-auto" ng-model="ctrl.current.jsonData.esVersion" ng-options="f.value as f.name for f in ctrl.esVersions"></select>
 	</div>
+    <div class="gf-form max-width-30" ng-if="ctrl.current.jsonData.esVersion>=56">
+        <span class="gf-form-label width-15">Max concurrent Shard Requests</span>
+        <input class="gf-form-input" type="text"  ng-model='ctrl.current.jsonData.maxConcurrentShardRequests' placeholder="" required></input>
+    </div>
 	<div class="gf-form-inline">
 		<div class="gf-form">
 			<span class="gf-form-label width-9">Min interval</span>

+ 0 - 981
public/app/plugins/datasource/graphite/gfunc.js

@@ -1,981 +0,0 @@
-define([
-  'lodash',
-  'jquery',
-  'app/core/utils/version'
-],
-function (_, $, version) {
-  'use strict';
-
-  var index = [];
-  var categories = {
-    Combine: [],
-    Transform: [],
-    Calculate: [],
-    Filter: [],
-    Special: []
-  };
-
-  function addFuncDef(funcDef) {
-    funcDef.params = funcDef.params || [];
-    funcDef.defaultParams = funcDef.defaultParams || [];
-
-    if (funcDef.category) {
-      funcDef.category.push(funcDef);
-    }
-    index[funcDef.name] = funcDef;
-    index[funcDef.shortName || funcDef.name] = funcDef;
-  }
-
-  var optionalSeriesRefArgs = [
-    { name: 'other', type: 'value_or_series', optional: true },
-    { name: 'other', type: 'value_or_series', optional: true },
-    { name: 'other', type: 'value_or_series', optional: true },
-    { name: 'other', type: 'value_or_series', optional: true },
-    { name: 'other', type: 'value_or_series', optional: true }
-  ];
-
-  addFuncDef({
-    name: 'scaleToSeconds',
-    category: categories.Transform,
-    params: [{ name: 'seconds', type: 'int' }],
-    defaultParams: [1],
-  });
-
-  addFuncDef({
-    name: 'perSecond',
-    category: categories.Transform,
-    params: [{ name: "max value", type: "int", optional: true }],
-    defaultParams: [],
-  });
-
-  addFuncDef({
-    name: "holtWintersForecast",
-    category: categories.Calculate,
-  });
-
-  addFuncDef({
-    name: "holtWintersConfidenceBands",
-    category: categories.Calculate,
-    params: [{ name: "delta", type: 'int' }],
-    defaultParams: [3]
-  });
-
-  addFuncDef({
-    name: "holtWintersAberration",
-    category: categories.Calculate,
-    params: [{ name: "delta", type: 'int' }],
-    defaultParams: [3]
-  });
-
-  addFuncDef({
-    name: "nPercentile",
-    category: categories.Calculate,
-    params: [{ name: "Nth percentile", type: 'int' }],
-    defaultParams: [95]
-  });
-
-  addFuncDef({
-    name: 'diffSeries',
-    params: optionalSeriesRefArgs,
-    defaultParams: ['#A'],
-    category: categories.Calculate,
-  });
-
-  addFuncDef({
-    name: 'stddevSeries',
-    params: optionalSeriesRefArgs,
-    defaultParams: [''],
-    category: categories.Calculate,
-  });
-
-  addFuncDef({
-    name: 'divideSeries',
-    params: optionalSeriesRefArgs,
-    defaultParams: ['#A'],
-    category: categories.Calculate,
-  });
-
-  addFuncDef({
-    name: 'multiplySeries',
-    params: optionalSeriesRefArgs,
-    defaultParams: ['#A'],
-    category: categories.Calculate,
-  });
-
-  addFuncDef({
-    name: 'asPercent',
-    params: optionalSeriesRefArgs,
-    defaultParams: ['#A'],
-    category: categories.Calculate,
-  });
-
-  addFuncDef({
-    name: 'group',
-    params: optionalSeriesRefArgs,
-    defaultParams: ['#A', '#B'],
-    category: categories.Combine,
-  });
-
-  addFuncDef({
-    name: 'sumSeries',
-    shortName: 'sum',
-    category: categories.Combine,
-    params: optionalSeriesRefArgs,
-    defaultParams: [''],
-  });
-
-  addFuncDef({
-    name: 'averageSeries',
-    shortName: 'avg',
-    category: categories.Combine,
-    params: optionalSeriesRefArgs,
-    defaultParams: [''],
-  });
-
-  addFuncDef({
-    name: 'rangeOfSeries',
-    category: categories.Combine
-  });
-
-  addFuncDef({
-    name: 'percentileOfSeries',
-    category: categories.Combine,
-    params: [{ name: 'n', type: 'int' }, { name: 'interpolate', type: 'boolean', options: ['true', 'false'] }],
-    defaultParams: [95, 'false']
-  });
-
-  addFuncDef({
-    name: 'sumSeriesWithWildcards',
-    category: categories.Combine,
-    params: [
-      { name: "node", type: "int" },
-      { name: "node", type: "int", optional: true },
-      { name: "node", type: "int", optional: true },
-      { name: "node", type: "int", optional: true }
-    ],
-    defaultParams: [3]
-  });
-
-  addFuncDef({
-    name: 'maxSeries',
-    shortName: 'max',
-    category: categories.Combine,
-  });
-
-  addFuncDef({
-    name: 'minSeries',
-    shortName: 'min',
-    category: categories.Combine,
-  });
-
-  addFuncDef({
-    name: 'averageSeriesWithWildcards',
-    category: categories.Combine,
-    params: [
-      { name: "node", type: "int" },
-      { name: "node", type: "int", optional: true },
-    ],
-    defaultParams: [3]
-  });
-
-  addFuncDef({
-    name: "alias",
-    category: categories.Special,
-    params: [{ name: "alias", type: 'string' }],
-    defaultParams: ['alias']
-  });
-
-  addFuncDef({
-    name: "aliasSub",
-    category: categories.Special,
-    params: [{ name: "search", type: 'string' }, { name: "replace", type: 'string' }],
-    defaultParams: ['', '\\1']
-  });
-
-  addFuncDef({
-    name: "stacked",
-    category: categories.Special,
-    params: [{ name: "stack", type: 'string' }],
-    defaultParams: ['stacked']
-  });
-
-  addFuncDef({
-    name: "consolidateBy",
-    category: categories.Special,
-    params: [
-      {
-        name: 'function',
-        type: 'string',
-        options: ['sum', 'average', 'min', 'max']
-      }
-    ],
-    defaultParams: ['max']
-  });
-
-  addFuncDef({
-    name: "cumulative",
-    category: categories.Special,
-    params: [],
-    defaultParams: []
-  });
-
-  addFuncDef({
-    name: "groupByNode",
-    category: categories.Special,
-    params: [
-      {
-        name: "node",
-        type: "int",
-        options: [0,1,2,3,4,5,6,7,8,9,10,12]
-      },
-      {
-        name: "function",
-        type: "string",
-        options: ['sum', 'avg', 'maxSeries']
-      }
-    ],
-    defaultParams: [3, "sum"]
-  });
-
-  addFuncDef({
-    name: 'aliasByNode',
-    category: categories.Special,
-    params: [
-      { name: "node", type: "int", options: [0,1,2,3,4,5,6,7,8,9,10,12] },
-      { name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true },
-      { name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true },
-      { name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true },
-    ],
-    defaultParams: [3]
-  });
-
-  addFuncDef({
-    name: 'substr',
-    category: categories.Special,
-    params: [
-      { name: "start", type: "int", options: [-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,12] },
-      { name: "stop", type: "int", options: [-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,12] },
-    ],
-    defaultParams: [0, 0]
-  });
-
-  addFuncDef({
-    name: 'sortByName',
-    category: categories.Special,
-    params: [{ name: 'natural', type: 'boolean', options: ['true', 'false'], optional: true }],
-    defaultParams: ['false']
-  });
-
-  addFuncDef({
-    name: 'sortByMaxima',
-    category: categories.Special
-  });
-
-  addFuncDef({
-    name: 'sortByMinima',
-    category: categories.Special
-  });
-
-  addFuncDef({
-    name: 'sortByTotal',
-    category: categories.Special
-  });
-
-  addFuncDef({
-    name: 'aliasByMetric',
-    category: categories.Special,
-  });
-
-  addFuncDef({
-    name: 'randomWalk',
-    fake: true,
-    category: categories.Special,
-    params: [{ name: "name", type: "string", }],
-    defaultParams: ['randomWalk']
-  });
-
-  addFuncDef({
-    name: 'countSeries',
-    category: categories.Special
-  });
-
-  addFuncDef({
-    name: 'constantLine',
-    category: categories.Special,
-    params: [{ name: "value", type: "int", }],
-    defaultParams: [10]
-  });
-
-  addFuncDef({
-    name: 'cactiStyle',
-    category: categories.Special,
-  });
-
-  addFuncDef({
-    name: 'keepLastValue',
-    category: categories.Special,
-    params: [{ name: "n", type: "int", }],
-    defaultParams: [100]
-  });
-
-  addFuncDef({
-    name: "changed",
-    category: categories.Special,
-    params: [],
-    defaultParams: []
-  });
-
-  addFuncDef({
-    name: 'scale',
-    category: categories.Transform,
-    params: [{ name: "factor", type: "int", }],
-    defaultParams: [1]
-  });
-
-  addFuncDef({
-    name: 'offset',
-    category: categories.Transform,
-    params: [{ name: "amount", type: "int", }],
-    defaultParams: [10]
-  });
-
-  addFuncDef({
-    name: 'transformNull',
-    category: categories.Transform,
-    params: [{ name: "amount", type: "int", }],
-    defaultParams: [0]
-  });
-
-  addFuncDef({
-    name: 'integral',
-    category: categories.Transform,
-  });
-
-  addFuncDef({
-    name: 'derivative',
-    category: categories.Transform,
-  });
-
-  addFuncDef({
-    name: 'nonNegativeDerivative',
-    category: categories.Transform,
-    params: [{ name: "max value or 0", type: "int", optional: true }],
-    defaultParams: ['']
-  });
-
-  addFuncDef({
-    name: 'timeShift',
-    category: categories.Transform,
-    params: [{ name: "amount", type: "select", options: ['1h', '6h', '12h', '1d', '2d', '7d', '14d', '30d'] }],
-    defaultParams: ['1d']
-  });
-
-  addFuncDef({
-    name: 'timeStack',
-    category: categories.Transform,
-    params: [
-      { name: "timeShiftUnit", type: "select", options: ['1h', '6h', '12h', '1d', '2d', '7d', '14d', '30d'] },
-      { name: "timeShiftStart", type: "int" },
-      { name: "timeShiftEnd", type: "int" }
-    ],
-    defaultParams: ['1d', 0, 7]
-  });
-
-  addFuncDef({
-    name: 'summarize',
-    category: categories.Transform,
-    params: [
-      { name: "interval", type: "string" },
-      { name: "func", type: "select", options: ['sum', 'avg', 'min', 'max', 'last'] },
-      { name: "alignToFrom", type: "boolean", optional: true, options: ['false', 'true'] },
-    ],
-    defaultParams: ['1h', 'sum', 'false']
-  });
-
-  addFuncDef({
-    name: 'smartSummarize',
-    category: categories.Transform,
-    params: [{ name: "interval", type: "string" }, { name: "func", type: "select", options: ['sum', 'avg', 'min', 'max', 'last'] }],
-    defaultParams: ['1h', 'sum']
-  });
-
-  addFuncDef({
-    name: 'absolute',
-    category: categories.Transform,
-  });
-
-  addFuncDef({
-    name: 'hitcount',
-    category: categories.Transform,
-    params: [{ name: "interval", type: "string" }],
-    defaultParams: ['10s']
-  });
-
-  addFuncDef({
-    name: 'log',
-    category: categories.Transform,
-    params: [{ name: "base", type: "int" }],
-    defaultParams: ['10']
-  });
-
-  addFuncDef({
-    name: 'averageAbove',
-    category: categories.Filter,
-    params: [{ name: "n", type: "int", }],
-    defaultParams: [25]
-  });
-
-  addFuncDef({
-    name: 'averageBelow',
-    category: categories.Filter,
-    params: [{ name: "n", type: "int", }],
-    defaultParams: [25]
-  });
-
-  addFuncDef({
-    name: 'currentAbove',
-    category: categories.Filter,
-    params: [{ name: "n", type: "int", }],
-    defaultParams: [25]
-  });
-
-  addFuncDef({
-    name: 'currentBelow',
-    category: categories.Filter,
-    params: [{ name: "n", type: "int", }],
-    defaultParams: [25]
-  });
-
-  addFuncDef({
-    name: 'maximumAbove',
-    category: categories.Filter,
-    params: [{ name: "value", type: "int" }],
-    defaultParams: [0]
-  });
-
-  addFuncDef({
-    name: 'maximumBelow',
-    category: categories.Filter,
-    params: [{ name: "value", type: "int" }],
-    defaultParams: [0]
-  });
-
-  addFuncDef({
-    name: 'minimumAbove',
-    category: categories.Filter,
-    params: [{ name: "value", type: "int" }],
-    defaultParams: [0]
-  });
-
-  addFuncDef({
-    name: 'minimumBelow',
-    category: categories.Filter,
-    params: [{ name: "value", type: "int" }],
-    defaultParams: [0]
-  });
-
-  addFuncDef({
-    name: 'limit',
-    category: categories.Filter,
-    params: [{ name: "n", type: "int" }],
-    defaultParams: [5]
-  });
-
-  addFuncDef({
-    name: 'mostDeviant',
-    category: categories.Filter,
-    params: [{ name: "n", type: "int" }],
-    defaultParams: [10]
-  });
-
-  addFuncDef({
-    name: "exclude",
-    category: categories.Filter,
-    params: [{ name: "exclude", type: 'string' }],
-    defaultParams: ['exclude']
-  });
-
-  addFuncDef({
-    name: 'highestCurrent',
-    category: categories.Filter,
-    params: [{ name: "count", type: "int" }],
-    defaultParams: [5]
-  });
-
-  addFuncDef({
-    name: 'highestMax',
-    category: categories.Filter,
-    params: [{ name: "count", type: "int" }],
-    defaultParams: [5]
-  });
-
-  addFuncDef({
-    name: 'lowestCurrent',
-    category: categories.Filter,
-    params: [{ name: "count", type: "int" }],
-    defaultParams: [5]
-  });
-
-  addFuncDef({
-    name: 'movingAverage',
-    category: categories.Filter,
-    params: [{ name: "windowSize", type: "int_or_interval", options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }],
-    defaultParams: [10]
-  });
-
-  addFuncDef({
-    name: 'movingMedian',
-    category: categories.Filter,
-    params: [{ name: "windowSize", type: "int_or_interval", options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }],
-    defaultParams: ['5']
-  });
-
-  addFuncDef({
-    name: 'stdev',
-    category: categories.Filter,
-    params: [{ name: "n", type: "int" }, { name: "tolerance", type: "int" }],
-    defaultParams: [5,0.1]
-  });
-
-  addFuncDef({
-    name: 'highestAverage',
-    category: categories.Filter,
-    params: [{ name: "count", type: "int" }],
-    defaultParams: [5]
-  });
-
-  addFuncDef({
-    name: 'lowestAverage',
-    category: categories.Filter,
-    params: [{ name: "count", type: "int" }],
-    defaultParams: [5]
-  });
-
-  addFuncDef({
-    name: 'removeAbovePercentile',
-    category: categories.Filter,
-    params: [{ name: "n", type: "int" }],
-    defaultParams: [5]
-  });
-
-  addFuncDef({
-    name: 'removeAboveValue',
-    category: categories.Filter,
-    params: [{ name: "n", type: "int" }],
-    defaultParams: [5]
-  });
-
-  addFuncDef({
-    name: 'removeBelowPercentile',
-    category: categories.Filter,
-    params: [{ name: "n", type: "int" }],
-    defaultParams: [5]
-  });
-
-  addFuncDef({
-    name: 'removeBelowValue',
-    category: categories.Filter,
-    params: [{ name: "n", type: "int" }],
-    defaultParams: [5]
-  });
-
-  addFuncDef({
-    name: 'useSeriesAbove',
-    category: categories.Filter,
-    params: [
-      { name: "value", type: "int" },
-      { name: "search", type: "string" },
-      { name: "replace", type: "string" }
-    ],
-    defaultParams: [0, 'search', 'replace']
-  });
-
-  ////////////////////
-  // Graphite 1.0.x //
-  ////////////////////
-
-  addFuncDef({
-    name: 'aggregateLine',
-    category: categories.Combine,
-    params: [{ name: "func", type: "select", options: ['sum', 'avg', 'min', 'max', 'last']}],
-    defaultParams: ['avg'],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'averageOutsidePercentile',
-    category: categories.Filter,
-    params: [{ name: "n", type: "int", }],
-    defaultParams: [95],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'delay',
-    category: categories.Transform,
-    params: [{ name: 'steps', type: 'int', }],
-    defaultParams: [1],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'exponentialMovingAverage',
-    category: categories.Calculate,
-    params: [{ name: 'windowSize', type: 'int_or_interval', options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }],
-    defaultParams: [10],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'fallbackSeries',
-    category: categories.Special,
-    params: [{ name: 'fallback', type: 'string' }],
-    defaultParams: ['constantLine(0)'],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: "grep",
-    category: categories.Filter,
-    params: [{ name: "grep", type: 'string' }],
-    defaultParams: ['grep'],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: "groupByNodes",
-    category: categories.Special,
-    params: [
-      {
-        name: "function",
-        type: "string",
-        options: ['sum', 'avg', 'maxSeries']
-      },
-      { name: "node", type: "int", options: [0,1,2,3,4,5,6,7,8,9,10,12] },
-      { name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true },
-      { name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true },
-      { name: "node", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true },
-    ],
-    defaultParams: ["sum", 3],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'integralByInterval',
-    category: categories.Transform,
-    params: [{ name: "intervalUnit", type: "select", options: ['1h', '6h', '12h', '1d', '2d', '7d', '14d', '30d'] }],
-    defaultParams: ['1d'],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'interpolate',
-    category: categories.Transform,
-    params: [{ name: 'limit', type: 'int', optional: true}],
-    defaultParams: [],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'invert',
-    category: categories.Transform,
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'isNonNull',
-    category: categories.Combine,
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'linearRegression',
-    category: categories.Calculate,
-    params: [
-      { name: "startSourceAt", type: "select", options: ['-1h', '-6h', '-12h', '-1d', '-2d', '-7d', '-14d', '-30d'], optional: true },
-      { name: "endSourceAt", type: "select", options: ['-1h', '-6h', '-12h', '-1d', '-2d', '-7d', '-14d', '-30d'], optional: true }
-    ],
-    defaultParams: [],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'mapSeries',
-    shortName: 'map',
-    params: [{ name: "node", type: 'int' }],
-    defaultParams: [3],
-    category: categories.Combine,
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'movingMin',
-    category: categories.Calculate,
-    params: [{ name: 'windowSize', type: 'int_or_interval', options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }],
-    defaultParams: [10],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'movingMax',
-    category: categories.Calculate,
-    params: [{ name: 'windowSize', type: 'int_or_interval', options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }],
-    defaultParams: [10],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'movingSum',
-    category: categories.Calculate,
-    params: [{ name: 'windowSize', type: 'int_or_interval', options: ['5', '7', '10', '5min', '10min', '30min', '1hour'] }],
-    defaultParams: [10],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: "multiplySeriesWithWildcards",
-    category: categories.Calculate,
-    params: [
-      { name: "position", type: "int", options: [0,1,2,3,4,5,6,7,8,9,10,12] },
-      { name: "position", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true },
-      { name: "position", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true },
-      { name: "position", type: "int", options: [0,-1,-2,-3,-4,-5,-6,-7], optional: true },
-    ],
-    defaultParams: [2],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'offsetToZero',
-    category: categories.Transform,
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'pow',
-    category: categories.Transform,
-    params: [{ name: 'factor', type: 'int' }],
-    defaultParams: [10],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'powSeries',
-    category: categories.Transform,
-    params: optionalSeriesRefArgs,
-    defaultParams: [''],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'reduceSeries',
-    shortName: 'reduce',
-    params: [
-      { name: "function", type: 'string', options: ['asPercent', 'diffSeries', 'divideSeries'] },
-      { name: "reduceNode", type: 'int', options: [0,1,2,3,4,5,6,7,8,9,10,11,12,13] },
-      { name: "reduceMatchers", type: 'string' },
-      { name: "reduceMatchers", type: 'string' },
-    ],
-    defaultParams: ['asPercent', 2, 'used_bytes', 'total_bytes'],
-    category: categories.Combine,
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'removeBetweenPercentile',
-    category: categories.Filter,
-    params: [{ name: "n", type: "int", }],
-    defaultParams: [95],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'removeEmptySeries',
-    category: categories.Filter,
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'squareRoot',
-    category: categories.Transform,
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'timeSlice',
-    category: categories.Transform,
-    params: [
-      { name: "startSliceAt", type: "select", options: ['-1h', '-6h', '-12h', '-1d', '-2d', '-7d', '-14d', '-30d']},
-      { name: "endSliceAt", type: "select", options: ['-1h', '-6h', '-12h', '-1d', '-2d', '-7d', '-14d', '-30d'], optional: true }
-    ],
-    defaultParams: ['-1h'],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'weightedAverage',
-    category: categories.Filter,
-    params: [
-      { name: 'other', type: 'value_or_series', optional: true },
-      { name: "node", type: "int", options: [0,1,2,3,4,5,6,7,8,9,10,12] },
-    ],
-    defaultParams: ['#A', 4],
-    version: '1.0'
-  });
-
-  addFuncDef({
-    name: 'seriesByTag',
-    category: categories.Special,
-    params: [
-      { name: "tagExpression", type: "string" },
-      { name: "tagExpression", type: "string", optional: true },
-      { name: "tagExpression", type: "string", optional: true },
-      { name: "tagExpression", type: "string", optional: true },
-    ],
-    version: '1.1'
-  });
-
-  addFuncDef({
-    name: "groupByTags",
-    category: categories.Special,
-    params: [
-      {
-        name: "function",
-        type: "string",
-        options: ['sum', 'avg', 'maxSeries']
-      },
-      { name: "tag", type: "string" },
-      { name: "tag", type: "string", optional: true },
-      { name: "tag", type: "string", optional: true },
-      { name: "tag", type: "string", optional: true },
-    ],
-    defaultParams: ["sum", "tag"],
-    version: '1.1'
-  });
-
-  addFuncDef({
-    name: "aliasByTags",
-    category: categories.Special,
-    params: [
-      { name: "tag", type: "string" },
-      { name: "tag", type: "string", optional: true },
-      { name: "tag", type: "string", optional: true },
-      { name: "tag", type: "string", optional: true },
-    ],
-    defaultParams: ["tag"],
-    version: '1.1'
-  });
-
-  _.each(categories, function(funcList, catName) {
-    categories[catName] = _.sortBy(funcList, 'name');
-  });
-
-  function FuncInstance(funcDef, options) {
-    this.def = funcDef;
-    this.params = [];
-
-    if (options && options.withDefaultParams) {
-      this.params = funcDef.defaultParams.slice(0);
-    }
-
-    this.updateText();
-  }
-
-  FuncInstance.prototype.render = function(metricExp) {
-    var str = this.def.name + '(';
-    var parameters = _.map(this.params, function(value, index) {
-
-      var paramType = this.def.params[index].type;
-      if (paramType === 'int' || paramType === 'value_or_series' || paramType === 'boolean') {
-        return value;
-      }
-      else if (paramType === 'int_or_interval' && $.isNumeric(value)) {
-        return value;
-      }
-
-      return "'" + value + "'";
-
-    }.bind(this));
-
-    if (metricExp) {
-      parameters.unshift(metricExp);
-    }
-
-    return str + parameters.join(', ') + ')';
-  };
-
-  FuncInstance.prototype._hasMultipleParamsInString = function(strValue, index) {
-    if (strValue.indexOf(',') === -1) {
-      return false;
-    }
-
-    return this.def.params[index + 1] && this.def.params[index + 1].optional;
-  };
-
-  FuncInstance.prototype.updateParam = function(strValue, index) {
-    // handle optional parameters
-    // if string contains ',' and next param is optional, split and update both
-    if (this._hasMultipleParamsInString(strValue, index)) {
-      _.each(strValue.split(','), function(partVal, idx) {
-        this.updateParam(partVal.trim(), index + idx);
-      }.bind(this));
-      return;
-    }
-
-    if (strValue === '' && this.def.params[index].optional) {
-      this.params.splice(index, 1);
-    }
-    else {
-      this.params[index] = strValue;
-    }
-
-    this.updateText();
-  };
-
-  FuncInstance.prototype.updateText = function () {
-    if (this.params.length === 0) {
-      this.text = this.def.name + '()';
-      return;
-    }
-
-    var text = this.def.name + '(';
-    text += this.params.join(', ');
-    text += ')';
-    this.text = text;
-  };
-
-  function isVersionRelatedFunction(func, graphiteVersion) {
-    return version.isVersionGtOrEq(graphiteVersion, func.version) || !func.version;
-  }
-
-  return {
-    createFuncInstance: function(funcDef, options) {
-      if (_.isString(funcDef)) {
-        if (!index[funcDef]) {
-          throw { message: 'Method not found ' + name };
-        }
-        funcDef = index[funcDef];
-      }
-      return new FuncInstance(funcDef, options);
-    },
-
-    getFuncDef: function(name) {
-      return index[name];
-    },
-
-    getCategories: function(graphiteVersion) {
-      var filteredCategories = {};
-      _.each(categories, function(functions, category) {
-        var filteredFuncs = _.filter(functions, function(func) {
-          return isVersionRelatedFunction(func, graphiteVersion);
-        });
-        if (filteredFuncs.length) {
-          filteredCategories[category] = filteredFuncs;
-        }
-      });
-
-      return filteredCategories;
-    }
-  };
-
-});

File diff suppressed because it is too large
+ 611 - 442
public/app/plugins/datasource/graphite/gfunc.ts


+ 0 - 136
public/app/plugins/datasource/prometheus/metric_find_query.js

@@ -1,136 +0,0 @@
-define([
-  'lodash'
-],
-function (_) {
-  'use strict';
-
-  function PrometheusMetricFindQuery(datasource, query, timeSrv) {
-    this.datasource = datasource;
-    this.query = query;
-    this.range = timeSrv.timeRange();
-  }
-
-  PrometheusMetricFindQuery.prototype.process = function() {
-    var label_values_regex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]+)\)$/;
-    var metric_names_regex = /^metrics\((.+)\)$/;
-    var query_result_regex = /^query_result\((.+)\)$/;
-
-    var label_values_query = this.query.match(label_values_regex);
-    if (label_values_query) {
-      if (label_values_query[1]) {
-        return this.labelValuesQuery(label_values_query[2], label_values_query[1]);
-      } else {
-        return this.labelValuesQuery(label_values_query[2], null);
-      }
-    }
-
-    var metric_names_query = this.query.match(metric_names_regex);
-    if (metric_names_query) {
-      return this.metricNameQuery(metric_names_query[1]);
-    }
-
-    var query_result_query = this.query.match(query_result_regex);
-    if (query_result_query) {
-      return this.queryResultQuery(query_result_query[1]);
-    }
-
-    // if query contains full metric name, return metric name and label list
-    return this.metricNameAndLabelsQuery(this.query);
-  };
-
-  PrometheusMetricFindQuery.prototype.labelValuesQuery = function(label, metric) {
-    var url;
-
-    if (!metric) {
-      // return label values globally
-      url = '/api/v1/label/' + label + '/values';
-
-      return this.datasource._request('GET', url).then(function(result) {
-        return _.map(result.data.data, function(value) {
-          return {text: value};
-        });
-      });
-    } else {
-      var start = this.datasource.getPrometheusTime(this.range.from, false);
-      var end = this.datasource.getPrometheusTime(this.range.to, true);
-      url = '/api/v1/series?match[]=' + encodeURIComponent(metric)
-        + '&start=' + start
-        + '&end=' + end;
-
-      return this.datasource._request('GET', url)
-      .then(function(result) {
-        var _labels = _.map(result.data.data, function(metric) {
-          return metric[label];
-        });
-
-        return _.uniq(_labels).map(function(metric) {
-          return {
-            text: metric,
-            expandable: true
-          };
-        });
-      });
-    }
-  };
-
-  PrometheusMetricFindQuery.prototype.metricNameQuery = function(metricFilterPattern) {
-    var url = '/api/v1/label/__name__/values';
-
-    return this.datasource._request('GET', url)
-    .then(function(result) {
-      return _.chain(result.data.data)
-      .filter(function(metricName) {
-        var r = new RegExp(metricFilterPattern);
-        return r.test(metricName);
-      })
-      .map(function(matchedMetricName) {
-        return {
-          text: matchedMetricName,
-          expandable: true
-        };
-      })
-      .value();
-    });
-  };
-
-  PrometheusMetricFindQuery.prototype.queryResultQuery = function(query) {
-    var end = this.datasource.getPrometheusTime(this.range.to, true);
-    return this.datasource.performInstantQuery({ expr: query }, end)
-    .then(function(result) {
-      return _.map(result.data.data.result, function(metricData) {
-        var text = metricData.metric.__name__ || '';
-        delete metricData.metric.__name__;
-        text += '{' +
-                _.map(metricData.metric, function(v, k) { return k + '="' + v + '"'; }).join(',') +
-                '}';
-        text += ' ' + metricData.value[1] + ' ' + metricData.value[0] * 1000;
-
-        return {
-          text: text,
-          expandable: true
-        };
-      });
-    });
-  };
-
-  PrometheusMetricFindQuery.prototype.metricNameAndLabelsQuery = function(query) {
-    var start = this.datasource.getPrometheusTime(this.range.from, false);
-    var end = this.datasource.getPrometheusTime(this.range.to, true);
-    var url = '/api/v1/series?match[]=' + encodeURIComponent(query)
-      + '&start=' + start
-      + '&end=' + end;
-
-    var self = this;
-    return this.datasource._request('GET', url)
-    .then(function(result) {
-      return _.map(result.data.data, function(metric) {
-        return {
-          text: self.datasource.getOriginalMetricName(metric),
-          expandable: true
-        };
-      });
-    });
-  };
-
-  return PrometheusMetricFindQuery;
-});

+ 147 - 0
public/app/plugins/datasource/prometheus/metric_find_query.ts

@@ -0,0 +1,147 @@
+import _ from "lodash";
+
+export default class PrometheusMetricFindQuery {
+  datasource: any;
+  query: any;
+  range: any;
+
+  constructor(datasource, query, timeSrv) {
+    this.datasource = datasource;
+    this.query = query;
+    this.range = timeSrv.timeRange();
+  }
+
+  process() {
+    var label_values_regex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]+)\)$/;
+    var metric_names_regex = /^metrics\((.+)\)$/;
+    var query_result_regex = /^query_result\((.+)\)$/;
+
+    var label_values_query = this.query.match(label_values_regex);
+    if (label_values_query) {
+      if (label_values_query[1]) {
+        return this.labelValuesQuery(
+          label_values_query[2],
+          label_values_query[1]
+        );
+      } else {
+        return this.labelValuesQuery(label_values_query[2], null);
+      }
+    }
+
+    var metric_names_query = this.query.match(metric_names_regex);
+    if (metric_names_query) {
+      return this.metricNameQuery(metric_names_query[1]);
+    }
+
+    var query_result_query = this.query.match(query_result_regex);
+    if (query_result_query) {
+      return this.queryResultQuery(query_result_query[1]);
+    }
+
+    // if query contains full metric name, return metric name and label list
+    return this.metricNameAndLabelsQuery(this.query);
+  }
+
+  labelValuesQuery(label, metric) {
+    var url;
+
+    if (!metric) {
+      // return label values globally
+      url = "/api/v1/label/" + label + "/values";
+
+      return this.datasource._request("GET", url).then(function(result) {
+        return _.map(result.data.data, function(value) {
+          return { text: value };
+        });
+      });
+    } else {
+      var start = this.datasource.getPrometheusTime(this.range.from, false);
+      var end = this.datasource.getPrometheusTime(this.range.to, true);
+      url =
+        "/api/v1/series?match[]=" +
+        encodeURIComponent(metric) +
+        "&start=" +
+        start +
+        "&end=" +
+        end;
+
+      return this.datasource._request("GET", url).then(function(result) {
+        var _labels = _.map(result.data.data, function(metric) {
+          return metric[label];
+        });
+
+        return _.uniq(_labels).map(function(metric) {
+          return {
+            text: metric,
+            expandable: true
+          };
+        });
+      });
+    }
+  }
+
+  metricNameQuery(metricFilterPattern) {
+    var url = "/api/v1/label/__name__/values";
+
+    return this.datasource._request("GET", url).then(function(result) {
+      return _.chain(result.data.data)
+        .filter(function(metricName) {
+          var r = new RegExp(metricFilterPattern);
+          return r.test(metricName);
+        })
+        .map(function(matchedMetricName) {
+          return {
+            text: matchedMetricName,
+            expandable: true
+          };
+        })
+        .value();
+    });
+  }
+
+  queryResultQuery(query) {
+    var end = this.datasource.getPrometheusTime(this.range.to, true);
+    return this.datasource
+      .performInstantQuery({ expr: query }, end)
+      .then(function(result) {
+        return _.map(result.data.data.result, function(metricData) {
+          var text = metricData.metric.__name__ || "";
+          delete metricData.metric.__name__;
+          text +=
+            "{" +
+            _.map(metricData.metric, function(v, k) {
+              return k + '="' + v + '"';
+            }).join(",") +
+            "}";
+          text += " " + metricData.value[1] + " " + metricData.value[0] * 1000;
+
+          return {
+            text: text,
+            expandable: true
+          };
+        });
+      });
+  }
+
+  metricNameAndLabelsQuery(query) {
+    var start = this.datasource.getPrometheusTime(this.range.from, false);
+    var end = this.datasource.getPrometheusTime(this.range.to, true);
+    var url =
+      "/api/v1/series?match[]=" +
+      encodeURIComponent(query) +
+      "&start=" +
+      start +
+      "&end=" +
+      end;
+
+    var self = this;
+    return this.datasource._request("GET", url).then(function(result) {
+      return _.map(result.data.data, function(metric) {
+        return {
+          text: self.datasource.getOriginalMetricName(metric),
+          expandable: true
+        };
+      });
+    });
+  }
+}

+ 24 - 25
public/app/plugins/panel/graph/series_overrides_ctrl.js → public/app/plugins/panel/graph/series_overrides_ctrl.ts

@@ -1,27 +1,24 @@
-define([
-  'angular',
-  'jquery',
-  'lodash',
-], function(angular, jquery, _) {
-  'use strict';
+import _ from 'lodash';
+import angular from 'angular';
 
-  var module = angular.module('grafana.controllers');
+export class SeriesOverridesCtrl {
 
-  module.controller('SeriesOverridesCtrl', function($scope, $element, popoverSrv) {
+  /** @ngInject */
+  constructor($scope, $element, popoverSrv) {
     $scope.overrideMenu = [];
     $scope.currentOverrides = [];
     $scope.override = $scope.override || {};
 
     $scope.addOverrideOption = function(name, propertyName, values) {
-      var option = {};
-      option.text = name;
-      option.propertyName = propertyName;
-      option.index = $scope.overrideMenu.length;
-      option.values = values;
-
-      option.submenu = _.map(values, function(value) {
-        return { text: String(value), value: value };
-      });
+      var option = {
+        text: name,
+        propertyName: propertyName,
+        index: $scope.overrideMenu.lenght,
+        values: values,
+        submenu: _.map(values, function(value) {
+          return { text: String(value), value: value };
+        })
+      };
 
       $scope.overrideMenu.push(option);
     };
@@ -97,22 +94,24 @@ define([
 
     $scope.addOverrideOption('Bars', 'bars', [true, false]);
     $scope.addOverrideOption('Lines', 'lines', [true, false]);
-    $scope.addOverrideOption('Line fill', 'fill', [0,1,2,3,4,5,6,7,8,9,10]);
-    $scope.addOverrideOption('Line width', 'linewidth', [0,1,2,3,4,5,6,7,8,9,10]);
+    $scope.addOverrideOption('Line fill', 'fill', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+    $scope.addOverrideOption('Line width', 'linewidth', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
     $scope.addOverrideOption('Null point mode', 'nullPointMode', ['connected', 'null', 'null as zero']);
     $scope.addOverrideOption('Fill below to', 'fillBelowTo', $scope.getSeriesNames());
     $scope.addOverrideOption('Staircase line', 'steppedLine', [true, false]);
     $scope.addOverrideOption('Dashes', 'dashes', [true, false]);
-    $scope.addOverrideOption('Dash Length', 'dashLength', [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]);
-    $scope.addOverrideOption('Dash Space', 'spaceLength', [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]);
+    $scope.addOverrideOption('Dash Length', 'dashLength', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]);
+    $scope.addOverrideOption('Dash Space', 'spaceLength', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]);
     $scope.addOverrideOption('Points', 'points', [true, false]);
-    $scope.addOverrideOption('Points Radius', 'pointradius', [1,2,3,4,5]);
+    $scope.addOverrideOption('Points Radius', 'pointradius', [1, 2, 3, 4, 5]);
     $scope.addOverrideOption('Stack', 'stack', [true, false, 'A', 'B', 'C', 'D']);
     $scope.addOverrideOption('Color', 'color', ['change']);
     $scope.addOverrideOption('Y-axis', 'yaxis', [1, 2]);
-    $scope.addOverrideOption('Z-index', 'zindex', [-3,-2,-1,0,1,2,3]);
+    $scope.addOverrideOption('Z-index', 'zindex', [-3, -2, -1, 0, 1, 2, 3]);
     $scope.addOverrideOption('Transform', 'transform', ['negative-Y']);
     $scope.addOverrideOption('Legend', 'legend', [true, false]);
     $scope.updateCurrentOverrides();
-  });
-});
+  }
+}
+
+angular.module('grafana.controllers').controller('SeriesOverridesCtrl', SeriesOverridesCtrl);

+ 1 - 1
tslint.json

@@ -17,7 +17,7 @@
     "max-line-length": [true, 150],
     "member-access": false,
     "no-arg": true,
-    "no-bitwise": true,
+    "no-bitwise": false,
     "no-console": [true,
       "debug",
       "info",

Some files were not shown because too many files changed in this diff