diff --git a/CHANGELOG.md b/CHANGELOG.md
index e93f3b8342104..99124fabcc3e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,87 @@
# 6.4.0 (unreleased)
-### Breaking changes
+# 6.4.0-beta1 (2019-09-17)
+
+### Features / Enhancements
+* **API**: Readonly datasources should not be created via the API. [#19006](https://github.com/grafana/grafana/pull/19006), [@papagian](https://github.com/papagian)
+* **Alerting**: Include configured AlertRuleTags in Webhooks notifier. [#18233](https://github.com/grafana/grafana/pull/18233), [@dominic-miglar](https://github.com/dominic-miglar)
+* **Annotations**: Add annotations support to Loki. [#18949](https://github.com/grafana/grafana/pull/18949), [@aocenas](https://github.com/aocenas)
+* **Annotations**: Use a single row to represent a region. [#17673](https://github.com/grafana/grafana/pull/17673), [@ryantxu](https://github.com/ryantxu)
+* **Auth**: Allow inviting existing users when login form is disabled. [#19048](https://github.com/grafana/grafana/pull/19048), [@548017](https://github.com/548017)
+* **Azure Monitor**: Add support for cross resource queries. [#19115](https://github.com/grafana/grafana/pull/19115), [@sunker](https://github.com/sunker)
+* **CLI**: Allow installing custom binary plugins. [#17551](https://github.com/grafana/grafana/pull/17551), [@aocenas](https://github.com/aocenas)
+* **Dashboard**: Adds Logs Panel (alpha) as visualization option for Dashboards. [#18641](https://github.com/grafana/grafana/pull/18641), [@hugohaggmark](https://github.com/hugohaggmark)
+* **Dashboard**: Reuse query results between panels . [#16660](https://github.com/grafana/grafana/pull/16660), [@ryantxu](https://github.com/ryantxu)
+* **Dashboard**: Set time to to 23:59:59 when setting To time using calendar. [#18595](https://github.com/grafana/grafana/pull/18595), [@simPod](https://github.com/simPod)
+* **DataLinks**: Add DataLinks support to Gauge, BarGauge and SingleStat2 panel. [#18605](https://github.com/grafana/grafana/pull/18605), [@ryantxu](https://github.com/ryantxu)
+* **DataLinks**: Enable access to labels & field names. [#18918](https://github.com/grafana/grafana/pull/18918), [@torkelo](https://github.com/torkelo)
+* **DataLinks**: Enable multiple data links per panel. [#18434](https://github.com/grafana/grafana/pull/18434), [@dprokop](https://github.com/dprokop)
+* **Docker**: switch docker image to alpine base with phantomjs support. [#18468](https://github.com/grafana/grafana/pull/18468), [@DanCech](https://github.com/DanCech)
+* **Elasticsearch**: allow templating queries to order by doc_count. [#18870](https://github.com/grafana/grafana/pull/18870), [@hackery](https://github.com/hackery)
+* **Explore**: Add throttling when doing live queries. [#19085](https://github.com/grafana/grafana/pull/19085), [@aocenas](https://github.com/aocenas)
+* **Explore**: Adds ability to go back to dashboard, optionally with query changes. [#17982](https://github.com/grafana/grafana/pull/17982), [@kaydelaney](https://github.com/kaydelaney)
+* **Explore**: Reduce default time range to last hour. [#18212](https://github.com/grafana/grafana/pull/18212), [@davkal](https://github.com/davkal)
+* **Gauge/BarGauge**: Support decimals for min/max. [#18368](https://github.com/grafana/grafana/pull/18368), [@ryantxu](https://github.com/ryantxu)
+* **Graph**: New series override transform constant that renders a single point as a line across the whole graph. [#19102](https://github.com/grafana/grafana/pull/19102), [@davkal](https://github.com/davkal)
+* **Image rendering**: Add deprecation warning when PhantomJS is used for rendering images. [#18933](https://github.com/grafana/grafana/pull/18933), [@papagian](https://github.com/papagian)
+* **InfluxDB**: Enable interpolation within ad-hoc filter values. [#18077](https://github.com/grafana/grafana/pull/18077), [@kvc-code](https://github.com/kvc-code)
+* **LDAP**: Allow an user to be synchronized against LDAP. [#18976](https://github.com/grafana/grafana/pull/18976), [@gotjosh](https://github.com/gotjosh)
+* **Ldap**: Add ldap debug page. [#18759](https://github.com/grafana/grafana/pull/18759), [@peterholmberg](https://github.com/peterholmberg)
+* **Loki**: Remove prefetching of default label values. [#18213](https://github.com/grafana/grafana/pull/18213), [@davkal](https://github.com/davkal)
+* **Metrics**: Add failed alert notifications metric. [#18089](https://github.com/grafana/grafana/pull/18089), [@koorgoo](https://github.com/koorgoo)
+* **OAuth**: Support JMES path lookup when retrieving user email. [#14683](https://github.com/grafana/grafana/pull/14683), [@bobmshannon](https://github.com/bobmshannon)
+* **OAuth**: return GitLab groups as a part of user info (enable team sync). [#18388](https://github.com/grafana/grafana/pull/18388), [@alexanderzobnin](https://github.com/alexanderzobnin)
+* **Panels**: Add unit for electrical charge - ampere-hour. [#18950](https://github.com/grafana/grafana/pull/18950), [@anirudh-ramesh](https://github.com/anirudh-ramesh)
+* **Plugin**: AzureMonitor - Reapply MetricNamespace support. [#17282](https://github.com/grafana/grafana/pull/17282), [@raphaelquati](https://github.com/raphaelquati)
+* **Plugins**: better warning when plugins fail to load. [#18671](https://github.com/grafana/grafana/pull/18671), [@ryantxu](https://github.com/ryantxu)
+* **Postgres**: Add support for scram sha 256 authentication. [#18397](https://github.com/grafana/grafana/pull/18397), [@nonamef](https://github.com/nonamef)
+* **RemoteCache**: Support SSL with Redis. [#18511](https://github.com/grafana/grafana/pull/18511), [@kylebrandt](https://github.com/kylebrandt)
+* **SingleStat**: The gauge option in now disabled/hidden (unless it's an old panel with it already enabled) . [#18610](https://github.com/grafana/grafana/pull/18610), [@ryantxu](https://github.com/ryantxu)
+* **Stackdriver**: Add extra alignment period options. [#18909](https://github.com/grafana/grafana/pull/18909), [@sunker](https://github.com/sunker)
+* **Units**: Add South African Rand (ZAR) to currencies. [#18893](https://github.com/grafana/grafana/pull/18893), [@jeteon](https://github.com/jeteon)
+* **Units**: Adding T,P,E,Z,and Y bytes. [#18706](https://github.com/grafana/grafana/pull/18706), [@chiqomar](https://github.com/chiqomar)
+
+### Bug Fixes
+* **Alerting**: Notification is sent when state changes from no_data to ok. [#18920](https://github.com/grafana/grafana/pull/18920), [@papagian](https://github.com/papagian)
+* **Alerting**: fix duplicate alert states when the alert fails to save to the database. [#18216](https://github.com/grafana/grafana/pull/18216), [@kylebrandt](https://github.com/kylebrandt)
+* **Alerting**: fix response popover prompt when add notification channels. [#18967](https://github.com/grafana/grafana/pull/18967), [@lzdw](https://github.com/lzdw)
+* **CloudWatch**: Fix alerting for queries with Id (using GetMetricData). [#17899](https://github.com/grafana/grafana/pull/17899), [@alex-berger](https://github.com/alex-berger)
+* **Explore**: Fix auto completion on label values for Loki. [#18988](https://github.com/grafana/grafana/pull/18988), [@aocenas](https://github.com/aocenas)
+* **Explore**: Fixes crash using back button with a zoomed in graph. [#19122](https://github.com/grafana/grafana/pull/19122), [@hugohaggmark](https://github.com/hugohaggmark)
+* **Explore**: Fixes so queries in Explore are only run if Graph/Table is shown. [#19000](https://github.com/grafana/grafana/pull/19000), [@hugohaggmark](https://github.com/hugohaggmark)
+* **MSSQL**: Change connectionstring to URL format to fix using passwords with semicolon. [#18384](https://github.com/grafana/grafana/pull/18384), [@Russiancold](https://github.com/Russiancold)
+* **MSSQL**: Fix memory leak when debug enabled. [#19049](https://github.com/grafana/grafana/pull/19049), [@briangann](https://github.com/briangann)
+* **Provisioning**: Allow escaping literal '$' with '$$' in configs to avoid interpolation. [#18045](https://github.com/grafana/grafana/pull/18045), [@kylebrandt](https://github.com/kylebrandt)
+* **TimePicker**: Fixes hiding time picker dropdown in FireFox. [#19154](https://github.com/grafana/grafana/pull/19154), [@hugohaggmark](https://github.com/hugohaggmark)
+
+## Breaking changes
+
+### Annotations
+
+There are some breaking changes in the annotations HTTP API for region annotations. Region annotations are now represented
+using a single event instead of two seperate events. Check breaking changes in HTTP API [below](#http-api) and [HTTP API documentation](https://grafana.com/docs/http_api/annotations/) for more details.
+
+### Docker
+
+Grafana is now using Alpine 3.10 as docker base image.
+
+### HTTP API
+ - `GET /api/alert-notifications` now requires at least editor access. New `/api/alert-notifications/lookup` returns less information than `/api/alert-notifications` and can be access by any authenticated user.
+ - `GET /api/alert-notifiers` now requires at least editor access
+ - `GET /api/org/users` now requires org admin role. New `/api/org/users/lookup` returns less information than `/api/org/users` and can be access by users that are org admins, admin in any folder or admin of any team.
+ - `GET /api/annotations` no longer returns `regionId` property.
+ - `POST /api/annotations` no longer supports `isRegion` property.
+ - `PUT /api/annotations/:id` no longer supports `isRegion` property.
+ - `PATCH /api/annotations/:id` no longer supports `isRegion` property.
+ - `DELETE /api/annotations/region/:id` has been removed.
+
+## Deprecation notes
+
+### PhantomJS
+
+[PhantomJS](https://phantomjs.org/), which is used for rendering images of dashboards and panels, is deprecated and will be removed in a future Grafana release. A deprecation warning will from now on be logged when Grafana starts up if PhantomJS is in use.
-* **Annotations**: There are some breaking changes in the annotations HTTP API for region annotations. Region
- annotations are now represented using a single event instead of two seperate events. Check HTTP docs for more details.
+Please consider migrating from PhantomJS to the [Grafana Image Renderer plugin](https://grafana.com/grafana/plugins/grafana-image-renderer).
# 6.3.5 (2019-09-02)
diff --git a/Dockerfile b/Dockerfile
index fefa8ff96f857..9ee46ad89a925 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -62,7 +62,8 @@ ENV PATH=/usr/share/grafana/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bi
WORKDIR $GF_PATHS_HOME
-RUN apk add --no-cache ca-certificates bash
+RUN apk add --no-cache ca-certificates bash && \
+ apk add --no-cache --upgrade --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main openssl musl-utils
COPY conf ./conf
diff --git a/ISSUE_TRIAGE.md b/ISSUE_TRIAGE.md
index 004419abe1214..cee5e8567eff0 100644
--- a/ISSUE_TRIAGE.md
+++ b/ISSUE_TRIAGE.md
@@ -183,6 +183,8 @@ If the author does not respond to the requested information within the timespan
When you feel you have all the information needed you're ready to [categorizing the issue](#3-categorizing-an-issue).
+If you receive a notification with additional information provided but you are not anymore on issue triage and you feel you do not have time to handle it, you should delegate it to the current person on issue triage.
+
## 3. Categorizing an issue
An issue can have multiple of the following labels. Typically, a properly categorized issue should at least have:
diff --git a/conf/defaults.ini b/conf/defaults.ini
index 52b9a4f2ed362..3bb57c3ac7201 100644
--- a/conf/defaults.ini
+++ b/conf/defaults.ini
@@ -593,8 +593,10 @@ enabled = true
#################################### Internal Grafana Metrics ############
# Metrics available at HTTP API Url /metrics
[metrics]
-enabled = true
-interval_seconds = 10
+enabled = true
+interval_seconds = 10
+# Disable total stats (stat_totals_*) metrics to be generated
+disable_total_stats = false
#If both are set, basic auth will be required for the metrics endpoint.
basic_auth_username =
@@ -667,8 +669,10 @@ container_name =
# does not require any configuration
[rendering]
-# Options to configure external image rendering server like https://github.com/grafana/grafana-image-renderer
+# Options to configure a remote HTTP image rendering service, e.g. using https://github.com/grafana/grafana-image-renderer.
+# URL to a remote HTTP image renderer service, e.g. http://localhost:8081/render, will enable Grafana to render panels and dashboards to PNG-images using HTTP requests to an external service.
server_url =
+# If the remote HTTP image renderer service runs on a different server than the Grafana server you may have to configure this to a URL where Grafana is reachable, e.g. http://grafana.domain/.
callback_url =
[panels]
diff --git a/conf/sample.ini b/conf/sample.ini
index 31468bfa15bc8..7e8d96065b9c2 100644
--- a/conf/sample.ini
+++ b/conf/sample.ini
@@ -526,6 +526,8 @@
[metrics]
# Disable / Enable internal metrics
;enabled = true
+# Disable total stats (stat_totals_*) metrics to be generated
+;disable_total_stats = false
# Publish interval
;interval_seconds = 10
@@ -596,8 +598,10 @@
# does not require any configuration
[rendering]
-# Options to configure external image rendering server like https://github.com/grafana/grafana-image-renderer
+# Options to configure a remote HTTP image rendering service, e.g. using https://github.com/grafana/grafana-image-renderer.
+# URL to a remote HTTP image renderer service, e.g. http://localhost:8081/render, will enable Grafana to render panels and dashboards to PNG-images using HTTP requests to an external service.
;server_url =
+# If the remote HTTP image renderer service runs on a different server than the Grafana server you may have to configure this to a URL where Grafana is reachable, e.g. http://grafana.domain/.
;callback_url =
[enterprise]
diff --git a/devenv/docker/blocks/slow_proxy_mac/Dockerfile b/devenv/docker/blocks/slow_proxy_mac/Dockerfile
new file mode 100644
index 0000000000000..e553cb6727c6d
--- /dev/null
+++ b/devenv/docker/blocks/slow_proxy_mac/Dockerfile
@@ -0,0 +1,7 @@
+
+FROM golang:latest
+ADD main.go /
+WORKDIR /
+RUN go build -o main .
+EXPOSE 3011
+ENTRYPOINT ["/main"]
diff --git a/devenv/docker/blocks/slow_proxy_mac/docker-compose.yaml b/devenv/docker/blocks/slow_proxy_mac/docker-compose.yaml
new file mode 100644
index 0000000000000..47347042df7fe
--- /dev/null
+++ b/devenv/docker/blocks/slow_proxy_mac/docker-compose.yaml
@@ -0,0 +1,6 @@
+ slow_proxy_mac:
+ build: docker/blocks/slow_proxy_mac
+ ports:
+ - '3011:3011'
+ environment:
+ ORIGIN_SERVER: 'http://host.docker.internal:9090/'
diff --git a/devenv/docker/blocks/slow_proxy_mac/main.go b/devenv/docker/blocks/slow_proxy_mac/main.go
new file mode 100644
index 0000000000000..dece2525c1399
--- /dev/null
+++ b/devenv/docker/blocks/slow_proxy_mac/main.go
@@ -0,0 +1,31 @@
+package main
+
+import (
+ "fmt"
+ "log"
+ "net/http"
+ "net/http/httputil"
+ "net/url"
+ "os"
+ "time"
+)
+
+func main() {
+ origin := os.Getenv("ORIGIN_SERVER")
+ if origin == "" {
+ origin = "http://host.docker.internal:9090/"
+ }
+
+ sleep := time.Minute
+
+ originURL, _ := url.Parse(origin)
+ proxy := httputil.NewSingleHostReverseProxy(originURL)
+
+ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ fmt.Printf("sleeping for %s then proxying request: %s", sleep.String(), r.RequestURI)
+ <-time.After(sleep)
+ proxy.ServeHTTP(w, r)
+ })
+
+ log.Fatal(http.ListenAndServe(":3011", nil))
+}
diff --git a/docs/sources/administration/image_rendering.md b/docs/sources/administration/image_rendering.md
new file mode 100644
index 0000000000000..1e1801e698741
--- /dev/null
+++ b/docs/sources/administration/image_rendering.md
@@ -0,0 +1,51 @@
++++
+title = "Image Rendering"
+description = ""
+keywords = ["grafana", "image", "rendering", "phantomjs"]
+type = "docs"
+aliases = ["/installation/image-rendering"]
+[menu.docs]
+parent = "admin"
+weight = 8
++++
+
+# Image Rendering
+
+Grafana supports rendering of panels and dasnhboards as PNG-images.
+
+When an image is being rendered the PNG-image is temporary written to the filesystem, i.e. a sub-directory of Grafana's [data](/installation/configuration/#data) directory named `png`.
+
+A background job runs each 10 minutes and will remove temporary images. You can configure how long time an image should be stored before being removed by configuring the [temp-data-lifetime](/installation/configuration/#temp-data-lifetime) setting.
+
+## Rendering methods
+
+### PhantomJS
+
+> PhantomJS is deprecated since Grafana v6.4 and will be removed in a future release. Please migrate to Grafana image renderer plugin or remote rendering service.
+
+[PhantomJS](https://phantomjs.org/) have been the only supported and default image renderer since Grafana v2.x and is shipped with Grafana.
+
+Please note that for OSX and Windows, you will need to ensure that a phantomjs binary is available under tools/phantomjs/phantomjs. For Linux, a phantomjs binary is included - however, you should ensure that any required libraries, e.g. libfontconfig1, are available.
+
+### Grafana image renderer plugin
+
+The [Grafana image renderer plugin](https://grafana.com/grafana/plugins/grafana-image-renderer) is a plugin that runs on the backend and handles rendering panels and dashboards as PNG-images using headless chrome.
+
+You can install it using grafana-cli:
+
+```bash
+grafana-cli plugins install grafana-image-renderer
+```
+
+For further information and instructions refer to the [plugin details](https://grafana.com/grafana/plugins/grafana-image-renderer).
+
+### Remote rendering service
+
+The [Grafana image renderer plugin](https://grafana.com/grafana/plugins/grafana-image-renderer) can also be run as a remote HTTP rendering service. In this setup Grafana will render an image by making a HTTP request to the remote rendering service, which in turn render the image and returns it back in the HTTP response to Grafana.
+
+To configure Grafana to use a remote HTTP rendering service, please refer to [rendering](/installation/configuration/#rendering) configuration section.
+
+## Alerting and render limits
+
+Alert notifications can include images, but rendering many images at the same time can overload the server where the renderer is running. For instructions of how to configure this, see [concurrent_render_limit](/installation/configuration/#concurrent-render-limit).
+
diff --git a/docs/sources/alerting/notifications.md b/docs/sources/alerting/notifications.md
index 7ec53e402b165..e2231986eecee 100644
--- a/docs/sources/alerting/notifications.md
+++ b/docs/sources/alerting/notifications.md
@@ -190,7 +190,9 @@ Webhook | `webhook` | yes, external only | yes
# Enable images in notifications {#external-image-store}
-Grafana can render the panel associated with the alert rule and include that in the notification. Most Notification Channels require that this image be publicly accessible (Slack and PagerDuty for example). In order to include images in alert notifications, Grafana can upload the image to an image store. It currently supports
+Grafana can render the panel associated with the alert rule as a PNG image and include that in the notification. Read more about the requirements and how to configure image rendering [here](/administration/image_rendering/).
+
+Most Notification Channels require that this image be publicly accessible (Slack and PagerDuty for example). In order to include images in alert notifications, Grafana can upload the image to an image store. It currently supports
Amazon S3, Webdav, Google Cloud Storage and Azure Blob Storage. So to set that up you need to configure the [external image uploader](/installation/configuration/#external-image-storage) in your grafana-server ini config file.
Be aware that some notifiers requires public access to the image to be able to include it in the notification. So make sure to enable public access to the images. If you're using local image uploader, your Grafana instance need to be accessible by the internet.
diff --git a/docs/sources/alerting/rules.md b/docs/sources/alerting/rules.md
index 510f466c3eb35..eebe7382c1d74 100644
--- a/docs/sources/alerting/rules.md
+++ b/docs/sources/alerting/rules.md
@@ -51,7 +51,9 @@ Here you can specify the name of the alert rule and how often the scheduler shou
### For
-> This setting is available in Grafana 5.4 and above.
+> **Important note regarding No Data:**
+>
+> Do not use `For` with the `If no data or all values are null` setting set to `No Data`. The triggering of `No Data` will trigger instantly and not take `For` into consideration. This may also result in that an OK notification not being sent if alert transitions from `No Data -> Pending -> OK`.
If an alert rule has a configured `For` and the query violates the configured threshold it will first go from `OK` to `Pending`. Going from `OK` to `Pending` Grafana will not send any notifications. Once the alert rule has been firing for more than `For` duration, it will change to `Alerting` and send alert notifications.
diff --git a/docs/sources/contribute/documentation.md b/docs/sources/contribute/documentation.md
new file mode 100644
index 0000000000000..391d996da4ec9
--- /dev/null
+++ b/docs/sources/contribute/documentation.md
@@ -0,0 +1,51 @@
++++
+title = "Documentation"
+description = "Contributing to documentation"
+type = "docs"
+[menu.docs]
+parent = "contribute"
+weight = 2
++++
+
+# Contributing to documentation
+
+## How do I contribute?
+
+If you’re unsure about where to start, check out some of our [open docs issues](https://github.com/grafana/grafana/issues?q=is%3Aopen+is%3Aissue+label%3Atype%2Fdocs).
+
+Sometimes it can be difficult to understand an issue when you're just getting started. We strive to keep a collection of [beginner-friendly issues](https://github.com/grafana/grafana/issues?q=is%3Aopen+is%3Aissue+label%3Atype%2Fdocs+label%3A"beginner+friendly") that is more suitable for first-time contributors.
+
+When you’ve found an issue you want to work on, you’re encouraged to comment on the issue to let other people know you intend to work on it.
+
+If you encounter any misspellings, or violations to the style guide, please let us know by submitting an issue.
+
+On every page in the documentation there are two links:
+
+- __Edit this page__ takes you directly to the file on GitHub where you can contribute a fix.
+- __Request doc changes__ prepares an issue on GitHub with relevant information already filled in.
+
+## Community
+
+If you have questions on a specific issue, post a comment to ask for clarification, or to give feedback.
+
+For general discussions on documentation, you’re welcome to join the `#docs` channel on our [public Grafana Slack](http://slack.raintank.io) team.
+
+## Guidelines
+
+All Grafana documentation is written using [Markdown](https://en.wikipedia.org/wiki/Markdown), and can be found in the [docs](https://github.com/grafana/grafana/tree/master/docs) directory in the [Grafana GitHub repository](https://github.com/grafana/grafana). The [documentation website](https://grafana.com/docs) is generated with [hugo](https://gohugo.io) which uses [Blackfriday](https://github.com/russross/blackfriday) as its Markdown rendering engine.
+
+### Structure
+
+The documentation is organized into topics, called _sections_.
+
+Each top-level section is located under the [docs/sources](https://github.com/grafana/grafana/tree/master/docs/sources) directory. Subsections are added by creating a subdirectory in the directory of the parent section.
+
+For each section, a `_index.md` file is used to provide an overview of the topic.
+
+### Style guide
+
+The [codespell](https://github.com/codespell-project/codespell) tool is run for every change to catch common misspellings.
+
+- "Open source" should be hyphenated when used as an adjective, e.g. _open-source software_. The open form should be preferred when used as a noun, e.g. _Grafana is open source_.
+- Use "data source" instead of "datasource" unless used as an identifier, in code or as part of URLs.
+- Acronyms should be uppercased, e.g. URL, DNS, or TCP/IP.
diff --git a/docs/sources/features/datasources/prometheus.md b/docs/sources/features/datasources/prometheus.md
index 5ac3543cb3112..e0124d76f017e 100644
--- a/docs/sources/features/datasources/prometheus.md
+++ b/docs/sources/features/datasources/prometheus.md
@@ -25,16 +25,17 @@ Grafana includes built-in support for Prometheus.
## Data source options
-| Name | Description |
-| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
-| _Name_ | The data source name. This is how you refer to the data source in panels & queries. |
-| _Default_ | Default data source means that it will be pre-selected for new panels. |
-| _Url_ | The http protocol, ip and port of you Prometheus server (default port is usually 9090) |
-| _Access_ | Server (default) = URL needs to be accessible from the Grafana backend/server, Browser = URL needs to be accessible from the browser. |
-| _Basic Auth_ | Enable basic authentication to the Prometheus data source. |
-| _User_ | Name of your Prometheus user |
-| _Password_ | Database user's password |
-| _Scrape interval_ | This will be used as a lower limit for the Prometheus step query parameter. Default value is 15s. |
+| Name | Description |
+| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
+| _Name_ | The data source name. This is how you refer to the data source in panels & queries. |
+| _Default_ | Default data source means that it will be pre-selected for new panels. |
+| _Url_ | The http protocol, ip and port of you Prometheus server (default port is usually 9090) |
+| _Access_ | Server (default) = URL needs to be accessible from the Grafana backend/server, Browser = URL needs to be accessible from the browser. |
+| _Basic Auth_ | Enable basic authentication to the Prometheus data source. |
+| _User_ | Name of your Prometheus user |
+| _Password_ | Database user's password |
+| _Scrape interval_ | This will be used as a lower limit for the Prometheus step query parameter. Default value is 15s. |
+| _CustomQueryParameters_ | Add Custom parameters to Prometheus query url. For example `timeout`, `partial_response`, `dedup` or `max_source_resolution`. |
## Query editor
diff --git a/docs/sources/features/panels/graph.md b/docs/sources/features/panels/graph.md
index 33a51274454d1..37eac3aec81f6 100644
--- a/docs/sources/features/panels/graph.md
+++ b/docs/sources/features/panels/graph.md
@@ -210,6 +210,9 @@ available suggestions:
#### Built-in variables
+> These variables changed in 6.4 so if you have an older version of Grafana please use the version picker to select
+docs for an older version of Grafana.
+
``__url_time_range`` - current dashboard's time range (i.e. ``?from=now-6h&to=now``)
``__from`` - current dashboard's time range from value
``__to`` - current dashboard's time range to value
@@ -240,7 +243,6 @@ Value specific variables are available under ``__value`` namespace:
``__value.calc`` - calculation name if the value is result of calculation
-
#### Template variables
When linking to another dashboard that uses template variables, you can use ``var-myvar=${myvar}`` syntax (where ``myvar`` is a name of template variable)
diff --git a/docs/sources/guides/whats-new-in-v6-4.md b/docs/sources/guides/whats-new-in-v6-4.md
new file mode 100644
index 0000000000000..e105c641ea9e4
--- /dev/null
+++ b/docs/sources/guides/whats-new-in-v6-4.md
@@ -0,0 +1,147 @@
++++
+title = "What's New in Grafana v6.4"
+description = "Feature & improvement highlights for Grafana v6.4"
+keywords = ["grafana", "new", "documentation", "6.4"]
+type = "docs"
+[menu.docs]
+name = "Version 6.4"
+identifier = "v6.4"
+parent = "whatsnew"
+weight = -15
++++
+
+# What's New in Grafana v6.4
+
+For all details please read the full [CHANGELOG.md](https://github.com/grafana/grafana/blob/master/CHANGELOG.md)
+
+## Highlights
+
+Grafana 6.4 comes with a lot of new features and enhancements backed with tons of work around the data models and query execution that is going to enable powerful future capabilities.
+Some of those new capabilities can already be seen in this release, like sharing query results between panels.
+
+- [**Explore:** Go back to dashboard (with query changes)]({{< relref "#go-back-to-dashboard-from-explore" >}})
+- [**Explore:** Live tailing improvements]({{< relref "#live-tailing-improvements" >}})
+- **Loki:** Show logs as annotations in dashboard graphs
+- **Loki:** Use Loki in dashboard panels
+- [**Panels:** New logs panel]({{< relref "#new-logs-panel" >}})
+- [**Panels:** Data links improvements]({{< relref "#data-links-improvements" >}})
+- **Graph:** Series override to turn constant (point) into a line
+- [**Dashboard:** Share query results between panels]({{< relref "#share-query-results-between-panels" >}})
+- [**Plugins:** Alpha version of grafana-toolkit]({{< relref "#alpha-version-of-grafana-toolkit" >}})
+- [**Azure:** Query over multiple resources in Azure Monitor]({{< relref "#query-over-multiple-resources-in-azure-monitor" >}})
+- [**ImageRendering:** PhantomJS deprecation]({{< relref "#phantomjs-deprecation" >}})
+- [**Docker:** Alpine based docker image]({{< relref "#alpine-based-docker-image" >}})
+- [**LDAP:** Debug UI]({{< relref "#ldap-debug-ui" >}})
+- [**Enterprise**: Reporting]({{< relref "#reporting" >}})
+- [**Enterprise**: GitLab OAuth Team Sync support]({{< relref "#gitlab-oauth-team-sync-support" >}})
+- [**Enterprise**: Teams & LDAP Improvements]({{< relref "#ldap-teams" >}})
+
+
+### Go back to dashboard from Explore
+
+To help accelerate workflows that involve regularly switching from Explore to a dashboard and vice-versa, we've added the ability to return to the origin dashboard
+after navigating to Explore from the panel's dropdown.
+
+{{< docs-imagebox img="/img/docs/v60/explore_panel_menu.png" caption="Screenshot of the new Explore Icon" >}}
+
+After you've navigated to Explore, you should notice a "Back" button in the Explore toolbar.
+
+
+
+Simply clicking the button will return you to the origin dashboard, or, if you'd like to bring changes you make in Explore back to the dashboard, simply click
+the arrow next to the button to reveal a "Return to panel with changes" menu item.
+
+
+
+### Live tailing improvements
+
+With 6.4 version you can now pause the live tail view to see the last 1000 lines of logs without being interrupted by new logs coming in. You can either pause manually with pause button or the live tailing will automatically pause when you scroll up to see older logs. To resume you just hit the resume button to continue live tailing.
+
+We also introduced some performance optimizations to allow live tailing of higher throughput log streams and various UI fixes and improvements like more consistent styling and fresh logs highlighting.
+
+
+
+### New Logs Panel
+
+A new panel specifically to show logs is added in this release. It’s in early alpha state so you have to enable alpha panels (in config file) to be able to try it out.
+
+## Data Links improvements
+
+With Grafana 6.3 we introduced a new way of creating [Data Links](https://grafana.com/blog/2019/08/27/new-in-grafana-6.3-easy-to-use-data-links/).
+Grafana 6.4 improves Data Links and adds them to the Gauge and Bar Gauge and panels.
+
+With Data Links you can define dynamic links to other dashboards and systems. The link can now reference template variables and query results like series name & labels, field name, value and time.
+
+Read more about Data Links and what you can do with them in [documentation](https://grafana.com/docs/features/panels/graph/#data-link)
+
+## Share query results between panels
+
+Grafana 6.4 continues the work started in 6.3 of creating a data model and query execution lifecycle that can support robust analytics and streaming. These changes are mostly structural and lay the foundation for powerful features in future releases.
+
+The first new feature all these changes have enabled is the ability to share query results between panels. So for example if you have an expensive query you can visualize the same results in a graph, table and singlestat panel. To reuse another panel’s query result select the data source named `-- Dashboard --` and then select the panel.
+
+To make the sharing of query results even more powerful we are introducing a transformation step as well that allows you to select specific parts of the query result and transform it. This new transformation feature is in [alpha](https://grafana.com/docs/installation/configuration/#enable-alpha) state and has to be enabled in the config file.
+
+DataFrame, our primary data model, has now a [columnar](https://en.wikipedia.org/wiki/Column-oriented_DBMS) layout. This
+will support easier frontend processing. The DataSource query interface has been updated to better support streaming.
+The result can now either return a `Promise` or `Observable`. Be on the lookout for more on live data
+streaming in the future!
+
+## Alpha version of grafana-toolkit
+
+[grafana-toolkit](https://www.npmjs.com/package/@grafana/toolkit/v/6.4.0-beta.1) is our attempt to simplify the life of plugin developers. It’s a CLI that helps them focus on the core value of their plugin rather than the ceremony around setting up the environment, configs, tests and builds. It’s available as an NPM package under `next` tag.
+
+You can read more about the grafana-toolkit [in the Readme](https://github.com/grafana/grafana/blob/master/packages/grafana-toolkit/README.md) and play with it by trying out our [react panel](https://github.com/grafana/simple-react-panel) or [angular panel](https://github.com/grafana/simple-angular-panel) templates.
+
+## Query over multiple resources in Azure Monitor
+
+Up until now it has only been possible to query over one resource in one subscription in the Azure Monitor datasource. In Grafana 6.4, the Azure Monitor query editor contains a new option called Query Mode which allows for querying over multiple resources in multiple subscriptions. This can be very useful in many situations, e.g cpu credits consumed by all virtual machines over many subscriptions.
+
+Alerting is yet to be implemented for Multiple Resources queries.
+
+## PhantomJS deprecation
+
+[PhantomJS](https://phantomjs.org/), which is used for rendering images of dashboards and panels, have been deprecated and will be removed in a future Grafana release. A deprecation warning will from now on be logged when Grafana starts up if PhantomJS is in use.
+
+Please consider migrating from PhantomJS to the [Grafana Image Renderer plugin](https://grafana.com/grafana/plugins/grafana-image-renderer).
+
+## Alpine based docker image
+
+Grafana’s docker image is now based on Alpine 3.10 and should from now on report zero vulnerabilities when scanning the image for security vulnerabilities.
+
+## LDAP Debug UI
+
+After listening to customer feedback, we have been working at improving the experience to set up authentication and synchronization with LDAP. We're happy to present the new LDAP Debug View.
+
+You'll be able to see how a user authenticating with LDAP would be mapped and whether your LDAP integration is working correctly. Furthermore, it provides a simpler method to test your integration with LDAP server(s) and have a clear view of how attributes are mapped between both systems.
+
+The feature is currently limited to Grafana Server Admins.
+
+For more information on how to use this new feature, follow the [guide](TBD).
+
+## Grafana Enterprise
+
+### Reporting
+
+A common request from Enterprise users have been to be able to set up reporting for Grafana, and now it’s here. A report is simply a PDF of a Grafana dashboard, outside of just generating a PDF you can set up a schedule so that you can get the report emailed to yourself (or whoever is interested) whenever it suits you.
+
+This feature is currently limited to Organization Admins.
+
+{{< docs-imagebox img="/img/docs/v64/reports.jpeg" max-width="500px" caption="Reporting" >}}
+
+### GitLab OAuth Team Sync support
+
+GitLab OAuth gets support for Team Sync, making it possible to synchronize your GitLab Groups with Teams in Grafana.
+
+[Read more about Team Sync](https://grafana.com/docs/auth/team-sync/)
+
+## Upgrading
+
+See [upgrade notes](/docs/installation/upgrading/#upgrading-to-v6-4).
+
+## Changelog
+
+Checkout the [CHANGELOG.md](https://github.com/grafana/grafana/blob/master/CHANGELOG.md) file for a complete list of new features, changes, and bug fixes.
+
+
+
diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md
index 3c4eceaaab68f..ade8cb296ce5a 100644
--- a/docs/sources/installation/configuration.md
+++ b/docs/sources/installation/configuration.md
@@ -548,6 +548,9 @@ If set configures the username to use for basic authentication on the metrics en
### basic_auth_password
If set configures the password to use for basic authentication on the metrics endpoint.
+### disable_total_stats
+If set to `true`, then total stats generation (`stat_totals_*` metrics) is disabled. The default is `false`.
+
### interval_seconds
Flush/Write interval when sending metrics to external TSDB. Defaults to 10s.
@@ -685,6 +688,17 @@ Default setting for alert notification timeout. Default value is `30`
Default setting for max attempts to sending alert notifications. Default value is `3`
+## [rendering]
+
+Options to configure a remote HTTP image rendering service, e.g. using https://github.com/grafana/grafana-image-renderer.
+
+### server_url
+
+URL to a remote HTTP image renderer service, e.g. http://localhost:8081/render, will enable Grafana to render panels and dashboards to PNG-images using HTTP requests to an external service.
+
+### callback_url
+
+If the remote HTTP image renderer service runs on a different server than the Grafana server you may have to configure this to a URL where Grafana is reachable, e.g. http://grafana.domain/.
## [panels]
@@ -699,6 +713,11 @@ is false. This settings was introduced in Grafana v6.0.
Set to true if you want to test alpha plugins that are not yet ready for general usage.
+## [feature_toggles]
+### enable
+
+Keys of alpha features to enable, separated by space. Available alpha features are: `transformations`
+
# Removed options
diff --git a/docs/sources/reference/sharing.md b/docs/sources/reference/sharing.md
index aebf76090f3a3..7f9599d004064 100644
--- a/docs/sources/reference/sharing.md
+++ b/docs/sources/reference/sharing.md
@@ -39,12 +39,12 @@ Click a panel title to open the panel menu, then click share in the panel menu t
### Direct Link Rendered Image
-You also get a link to service side rendered PNG of the panel. Useful if you want to share an image of the panel. Please note that for OSX and Windows, you will need to ensure that a `phantomjs` binary is available under `tools/phantomjs/phantomjs`. For Linux, a `phantomjs` binary is included - however, you should ensure that any requisite libraries (e.g. libfontconfig1) are available.
+You also get a link to render a PNG image of the panel. Useful if you want to share an image of the panel. Read more about the requirements and how to configure image rendering [here](/administration/image_rendering/).
Example of a link to a server-side rendered PNG:
```bash
-http://play.grafana.org/render/dashboard-solo/db/grafana-play-home?orgId=1&panelId=4&from=1499272191563&to=1499279391563&width=1000&height=500&tz=UTC%2B02%3A00&timeout=5000
+https://play.grafana.org/d/000000012/grafana-play-home?orgId=1&from=1568719680173&to=1568726880174&panelId=4&fullscreen
```
#### Query String Parameters For Server-Side Rendered Images
diff --git a/latest.json b/latest.json
index 40d26f46fe2dc..b5abed00efaf0 100644
--- a/latest.json
+++ b/latest.json
@@ -1,4 +1,4 @@
{
"stable": "6.3.5",
- "testing": "6.3.5"
+ "testing": "6.4.0-beta1"
}
diff --git a/lerna.json b/lerna.json
index 30f4e4728fd69..28adbc5e7544a 100644
--- a/lerna.json
+++ b/lerna.json
@@ -2,5 +2,5 @@
"npmClient": "yarn",
"useWorkspaces": true,
"packages": ["packages/*"],
- "version": "6.4.0-pre"
+ "version": "6.5.0-pre"
}
diff --git a/package.json b/package.json
index 2e6792aaee971..381021a34533b 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"license": "Apache-2.0",
"private": true,
"name": "grafana",
- "version": "6.4.0-pre",
+ "version": "6.5.0-pre",
"repository": {
"type": "git",
"url": "http://github.com/grafana/grafana.git"
@@ -26,6 +26,7 @@
"@types/enzyme-adapter-react-16": "1.0.5",
"@types/expect-puppeteer": "3.3.1",
"@types/file-saver": "2.0.1",
+ "@types/hoist-non-react-statics": "3.3.0",
"@types/is-hotkey": "0.1.1",
"@types/jest": "24.0.13",
"@types/jquery": "1.10.35",
@@ -208,6 +209,7 @@
"eventemitter3": "2.0.3",
"fast-text-encoding": "^1.0.0",
"file-saver": "1.3.8",
+ "hoist-non-react-statics": "3.3.0",
"immutable": "3.8.2",
"is-hotkey": "0.1.4",
"jquery": "3.4.1",
diff --git a/packages/grafana-data/src/dataframe/CircularDataFrame.ts b/packages/grafana-data/src/dataframe/CircularDataFrame.ts
new file mode 100644
index 0000000000000..890be6c2f50b8
--- /dev/null
+++ b/packages/grafana-data/src/dataframe/CircularDataFrame.ts
@@ -0,0 +1,22 @@
+import { MutableDataFrame } from './MutableDataFrame';
+import { CircularVector } from '../vector/CircularVector';
+
+interface CircularOptions {
+ append?: 'head' | 'tail';
+ capacity?: number;
+}
+
+/**
+ * This dataframe can have values constantly added, and will never
+ * exceed the given capacity
+ */
+export class CircularDataFrame extends MutableDataFrame {
+ constructor(options: CircularOptions) {
+ super(undefined, (buffer?: any[]) => {
+ return new CircularVector({
+ ...options,
+ buffer,
+ });
+ });
+ }
+}
diff --git a/packages/grafana-data/src/utils/dataFrameView.test.ts b/packages/grafana-data/src/dataframe/DataFrameView.test.ts
similarity index 90%
rename from packages/grafana-data/src/utils/dataFrameView.test.ts
rename to packages/grafana-data/src/dataframe/DataFrameView.test.ts
index 8755cfc827a9b..eb182e60c396a 100644
--- a/packages/grafana-data/src/utils/dataFrameView.test.ts
+++ b/packages/grafana-data/src/dataframe/DataFrameView.test.ts
@@ -1,7 +1,7 @@
-import { FieldType, DataFrameDTO } from '../types/index';
-import { MutableDataFrame } from './dataFrameHelper';
-import { DataFrameView } from './dataFrameView';
-import { DateTime } from './moment_wrapper';
+import { FieldType, DataFrameDTO } from '../types/dataFrame';
+import { DateTime } from '../datetime/moment_wrapper';
+import { MutableDataFrame } from './MutableDataFrame';
+import { DataFrameView } from './DataFrameView';
interface MySpecialObject {
time: DateTime;
diff --git a/packages/grafana-data/src/utils/dataFrameView.ts b/packages/grafana-data/src/dataframe/DataFrameView.ts
similarity index 95%
rename from packages/grafana-data/src/utils/dataFrameView.ts
rename to packages/grafana-data/src/dataframe/DataFrameView.ts
index 1cf833e9dedc6..cbb0106183c1b 100644
--- a/packages/grafana-data/src/utils/dataFrameView.ts
+++ b/packages/grafana-data/src/dataframe/DataFrameView.ts
@@ -1,4 +1,5 @@
-import { DataFrame, Vector } from '../types/index';
+import { Vector } from '../types/vector';
+import { DataFrame } from '../types/dataFrame';
/**
* This abstraction will present the contents of a DataFrame as if
diff --git a/packages/grafana-data/src/utils/dataFrameHelper.test.ts b/packages/grafana-data/src/dataframe/FieldCache.test.ts
similarity index 56%
rename from packages/grafana-data/src/utils/dataFrameHelper.test.ts
rename to packages/grafana-data/src/dataframe/FieldCache.test.ts
index 5555c09cd4011..d38946bd712fc 100644
--- a/packages/grafana-data/src/utils/dataFrameHelper.test.ts
+++ b/packages/grafana-data/src/dataframe/FieldCache.test.ts
@@ -1,30 +1,7 @@
-import { DataFrameDTO, FieldType } from '../types';
-import { FieldCache, MutableDataFrame } from './dataFrameHelper';
+import { FieldCache } from './FieldCache';
+import { FieldType } from '../types/dataFrame';
import { toDataFrame } from './processDataFrame';
-describe('dataFrameHelper', () => {
- const frame = toDataFrame({
- fields: [
- { name: 'time', type: FieldType.time, values: [100, 200, 300] },
- { name: 'name', type: FieldType.string, values: ['a', 'b', 'c'] },
- { name: 'value', type: FieldType.number, values: [1, 2, 3] },
- { name: 'value', type: FieldType.number, values: [4, 5, 6] },
- ],
- });
- const ext = new FieldCache(frame);
-
- it('should get the first field with a duplicate name', () => {
- const field = ext.getFieldByName('value');
- expect(field!.name).toEqual('value');
- expect(field!.values.toJSON()).toEqual([1, 2, 3]);
- });
-
- it('should return index of the field', () => {
- const field = ext.getFirstFieldOfType(FieldType.number);
- expect(field!.index).toEqual(2);
- });
-});
-
describe('FieldCache', () => {
it('when creating a new FieldCache from fields should be able to query cache', () => {
const frame = toDataFrame({
@@ -90,68 +67,27 @@ describe('FieldCache', () => {
expect(fieldCache.getFieldByName('undefined')!.name).toEqual(expectedFieldNames[5]);
expect(fieldCache.getFieldByName('null')).toBeUndefined();
});
-});
-
-describe('reverse', () => {
- describe('when called with a DataFrame', () => {
- it('then it should reverse the order of values in all fields', () => {
- const frame: DataFrameDTO = {
- fields: [
- { name: 'time', type: FieldType.time, values: [100, 200, 300] },
- { name: 'name', type: FieldType.string, values: ['a', 'b', 'c'] },
- { name: 'value', type: FieldType.number, values: [1, 2, 3] },
- ],
- };
-
- const helper = new MutableDataFrame(frame);
-
- expect(helper.values.time.toArray()).toEqual([100, 200, 300]);
- expect(helper.values.name.toArray()).toEqual(['a', 'b', 'c']);
- expect(helper.values.value.toArray()).toEqual([1, 2, 3]);
-
- helper.reverse();
-
- expect(helper.values.time.toArray()).toEqual([300, 200, 100]);
- expect(helper.values.name.toArray()).toEqual(['c', 'b', 'a']);
- expect(helper.values.value.toArray()).toEqual([3, 2, 1]);
- });
- });
-});
-describe('Apending DataFrame', () => {
- it('Should append values', () => {
- const dto: DataFrameDTO = {
+ describe('field retrieval', () => {
+ const frame = toDataFrame({
fields: [
- { name: 'time', type: FieldType.time, values: [100] },
- { name: 'name', type: FieldType.string, values: ['a', 'b'] },
+ { name: 'time', type: FieldType.time, values: [100, 200, 300] },
+ { name: 'name', type: FieldType.string, values: ['a', 'b', 'c'] },
{ name: 'value', type: FieldType.number, values: [1, 2, 3] },
+ { name: 'value', type: FieldType.number, values: [4, 5, 6] },
],
- };
-
- const frame = new MutableDataFrame(dto);
- expect(frame.values.time.toArray()).toEqual([100, null, null]);
-
- // Set a value on the second row
- frame.set(1, { time: 200, name: 'BB', value: 20 });
- expect(frame.toArray()).toEqual([
- { time: 100, name: 'a', value: 1 }, // 1
- { time: 200, name: 'BB', value: 20 }, // 2
- { time: null, name: null, value: 3 }, // 3
- ]);
+ });
+ const ext = new FieldCache(frame);
- // Set a value on the second row
- frame.add({ value2: 'XXX' }, true);
- expect(frame.toArray()).toEqual([
- { time: 100, name: 'a', value: 1, value2: null }, // 1
- { time: 200, name: 'BB', value: 20, value2: null }, // 2
- { time: null, name: null, value: 3, value2: null }, // 3
- { time: null, name: null, value: null, value2: 'XXX' }, // 4
- ]);
+ it('should get the first field with a duplicate name', () => {
+ const field = ext.getFieldByName('value');
+ expect(field!.name).toEqual('value');
+ expect(field!.values.toJSON()).toEqual([1, 2, 3]);
+ });
- // Make sure length survives a spread operator
- const keys = Object.keys(frame);
- const copy = { ...frame } as any;
- expect(keys).toContain('length');
- expect(copy.length).toEqual(frame.length);
+ it('should return index of the field', () => {
+ const field = ext.getFirstFieldOfType(FieldType.number);
+ expect(field!.index).toEqual(2);
+ });
});
});
diff --git a/packages/grafana-data/src/dataframe/FieldCache.ts b/packages/grafana-data/src/dataframe/FieldCache.ts
new file mode 100644
index 0000000000000..07f6941e21dea
--- /dev/null
+++ b/packages/grafana-data/src/dataframe/FieldCache.ts
@@ -0,0 +1,78 @@
+import { Field, DataFrame, FieldType, guessFieldTypeForField } from '../index';
+
+interface FieldWithIndex extends Field {
+ index: number;
+}
+
+export class FieldCache {
+ fields: FieldWithIndex[] = [];
+
+ private fieldByName: { [key: string]: FieldWithIndex } = {};
+ private fieldByType: { [key: string]: FieldWithIndex[] } = {};
+
+ constructor(data: DataFrame) {
+ this.fields = data.fields.map((field, idx) => ({
+ ...field,
+ index: idx,
+ }));
+
+ for (let i = 0; i < data.fields.length; i++) {
+ const field = data.fields[i];
+ // Make sure it has a type
+ if (field.type === FieldType.other) {
+ const t = guessFieldTypeForField(field);
+ if (t) {
+ field.type = t;
+ }
+ }
+ if (!this.fieldByType[field.type]) {
+ this.fieldByType[field.type] = [];
+ }
+ this.fieldByType[field.type].push({
+ ...field,
+ index: i,
+ });
+
+ if (this.fieldByName[field.name]) {
+ console.warn('Duplicate field names in DataFrame: ', field.name);
+ } else {
+ this.fieldByName[field.name] = { ...field, index: i };
+ }
+ }
+ }
+
+ getFields(type?: FieldType): FieldWithIndex[] {
+ if (!type) {
+ return [...this.fields]; // All fields
+ }
+ const fields = this.fieldByType[type];
+ if (fields) {
+ return [...fields];
+ }
+ return [];
+ }
+
+ hasFieldOfType(type: FieldType): boolean {
+ const types = this.fieldByType[type];
+ return types && types.length > 0;
+ }
+
+ getFirstFieldOfType(type: FieldType): FieldWithIndex | undefined {
+ const arr = this.fieldByType[type];
+ if (arr && arr.length > 0) {
+ return arr[0];
+ }
+ return undefined;
+ }
+
+ hasFieldNamed(name: string): boolean {
+ return !!this.fieldByName[name];
+ }
+
+ /**
+ * Returns the first field with the given name.
+ */
+ getFieldByName(name: string): FieldWithIndex | undefined {
+ return this.fieldByName[name];
+ }
+}
diff --git a/packages/grafana-data/src/dataframe/MutableDataFrame.test.ts b/packages/grafana-data/src/dataframe/MutableDataFrame.test.ts
new file mode 100644
index 0000000000000..eb6f8aaee432f
--- /dev/null
+++ b/packages/grafana-data/src/dataframe/MutableDataFrame.test.ts
@@ -0,0 +1,66 @@
+import { DataFrameDTO, FieldType } from '../types/dataFrame';
+import { MutableDataFrame } from './MutableDataFrame';
+
+describe('Reversing DataFrame', () => {
+ describe('when called with a DataFrame', () => {
+ it('then it should reverse the order of values in all fields', () => {
+ const frame: DataFrameDTO = {
+ fields: [
+ { name: 'time', type: FieldType.time, values: [100, 200, 300] },
+ { name: 'name', type: FieldType.string, values: ['a', 'b', 'c'] },
+ { name: 'value', type: FieldType.number, values: [1, 2, 3] },
+ ],
+ };
+
+ const helper = new MutableDataFrame(frame);
+
+ expect(helper.values.time.toArray()).toEqual([100, 200, 300]);
+ expect(helper.values.name.toArray()).toEqual(['a', 'b', 'c']);
+ expect(helper.values.value.toArray()).toEqual([1, 2, 3]);
+
+ helper.reverse();
+
+ expect(helper.values.time.toArray()).toEqual([300, 200, 100]);
+ expect(helper.values.name.toArray()).toEqual(['c', 'b', 'a']);
+ expect(helper.values.value.toArray()).toEqual([3, 2, 1]);
+ });
+ });
+});
+
+describe('Apending DataFrame', () => {
+ it('Should append values', () => {
+ const dto: DataFrameDTO = {
+ fields: [
+ { name: 'time', type: FieldType.time, values: [100] },
+ { name: 'name', type: FieldType.string, values: ['a', 'b'] },
+ { name: 'value', type: FieldType.number, values: [1, 2, 3] },
+ ],
+ };
+
+ const frame = new MutableDataFrame(dto);
+ expect(frame.values.time.toArray()).toEqual([100, null, null]);
+
+ // Set a value on the second row
+ frame.set(1, { time: 200, name: 'BB', value: 20 });
+ expect(frame.toArray()).toEqual([
+ { time: 100, name: 'a', value: 1 }, // 1
+ { time: 200, name: 'BB', value: 20 }, // 2
+ { time: null, name: null, value: 3 }, // 3
+ ]);
+
+ // Set a value on the second row
+ frame.add({ value2: 'XXX' }, true);
+ expect(frame.toArray()).toEqual([
+ { time: 100, name: 'a', value: 1, value2: null }, // 1
+ { time: 200, name: 'BB', value: 20, value2: null }, // 2
+ { time: null, name: null, value: 3, value2: null }, // 3
+ { time: null, name: null, value: null, value2: 'XXX' }, // 4
+ ]);
+
+ // Make sure length survives a spread operator
+ const keys = Object.keys(frame);
+ const copy = { ...frame } as any;
+ expect(keys).toContain('length');
+ expect(copy.length).toEqual(frame.length);
+ });
+});
diff --git a/packages/grafana-data/src/utils/dataFrameHelper.ts b/packages/grafana-data/src/dataframe/MutableDataFrame.ts
similarity index 66%
rename from packages/grafana-data/src/utils/dataFrameHelper.ts
rename to packages/grafana-data/src/dataframe/MutableDataFrame.ts
index 53eff25aaa05a..9ff9aa0f3aefb 100644
--- a/packages/grafana-data/src/utils/dataFrameHelper.ts
+++ b/packages/grafana-data/src/dataframe/MutableDataFrame.ts
@@ -1,111 +1,12 @@
-import { Field, FieldType, DataFrame, Vector, FieldDTO, DataFrameDTO } from '../types/dataFrame';
-import { Labels, QueryResultMeta, KeyValue } from '../types/data';
-import { guessFieldTypeForField, guessFieldTypeFromValue, toDataFrameDTO } from './processDataFrame';
-import { ArrayVector, MutableVector, vectorToArray, CircularVector } from './vector';
+import { Field, DataFrame, DataFrameDTO, FieldDTO, FieldType } from '../types/dataFrame';
+import { KeyValue, QueryResultMeta, Labels } from '../types/data';
+import { guessFieldTypeFromValue, guessFieldTypeForField, toDataFrameDTO } from './processDataFrame';
import isArray from 'lodash/isArray';
import isString from 'lodash/isString';
-
-interface FieldWithIndex extends Field {
- index: number;
-}
-export class FieldCache {
- fields: FieldWithIndex[] = [];
-
- private fieldByName: { [key: string]: FieldWithIndex } = {};
- private fieldByType: { [key: string]: FieldWithIndex[] } = {};
-
- constructor(data: DataFrame) {
- this.fields = data.fields.map((field, idx) => ({
- ...field,
- index: idx,
- }));
-
- for (let i = 0; i < data.fields.length; i++) {
- const field = data.fields[i];
- // Make sure it has a type
- if (field.type === FieldType.other) {
- const t = guessFieldTypeForField(field);
- if (t) {
- field.type = t;
- }
- }
- if (!this.fieldByType[field.type]) {
- this.fieldByType[field.type] = [];
- }
- this.fieldByType[field.type].push({
- ...field,
- index: i,
- });
-
- if (this.fieldByName[field.name]) {
- console.warn('Duplicate field names in DataFrame: ', field.name);
- } else {
- this.fieldByName[field.name] = { ...field, index: i };
- }
- }
- }
-
- getFields(type?: FieldType): FieldWithIndex[] {
- if (!type) {
- return [...this.fields]; // All fields
- }
- const fields = this.fieldByType[type];
- if (fields) {
- return [...fields];
- }
- return [];
- }
-
- hasFieldOfType(type: FieldType): boolean {
- const types = this.fieldByType[type];
- return types && types.length > 0;
- }
-
- getFirstFieldOfType(type: FieldType): FieldWithIndex | undefined {
- const arr = this.fieldByType[type];
- if (arr && arr.length > 0) {
- return arr[0];
- }
- return undefined;
- }
-
- hasFieldNamed(name: string): boolean {
- return !!this.fieldByName[name];
- }
-
- /**
- * Returns the first field with the given name.
- */
- getFieldByName(name: string): FieldWithIndex | undefined {
- return this.fieldByName[name];
- }
-}
-
-function makeFieldParser(value: any, field: Field): (value: string) => any {
- if (!field.type) {
- if (field.name === 'time' || field.name === 'Time') {
- field.type = FieldType.time;
- } else {
- field.type = guessFieldTypeFromValue(value);
- }
- }
-
- if (field.type === FieldType.number) {
- return (value: string) => {
- return parseFloat(value);
- };
- }
-
- // Will convert anything that starts with "T" to true
- if (field.type === FieldType.boolean) {
- return (value: string) => {
- return !(value[0] === 'F' || value[0] === 'f' || value[0] === '0');
- };
- }
-
- // Just pass the string back
- return (value: string) => value;
-}
+import { makeFieldParser } from '../utils/fieldParser';
+import { MutableVector, Vector } from '../types/vector';
+import { ArrayVector } from '../vector/ArrayVector';
+import { vectorToArray } from '../vector/vectorToArray';
export type MutableField = Field>;
@@ -380,23 +281,3 @@ export class MutableDataFrame implements DataFrame, MutableVector {
return toDataFrameDTO(this);
}
}
-
-interface CircularOptions {
- append?: 'head' | 'tail';
- capacity?: number;
-}
-
-/**
- * This dataframe can have values constantly added, and will never
- * exceed the given capacity
- */
-export class CircularDataFrame extends MutableDataFrame {
- constructor(options: CircularOptions) {
- super(undefined, (buffer?: any[]) => {
- return new CircularVector({
- ...options,
- buffer,
- });
- });
- }
-}
diff --git a/packages/grafana-data/src/dataframe/index.ts b/packages/grafana-data/src/dataframe/index.ts
new file mode 100644
index 0000000000000..e2cfc615ce434
--- /dev/null
+++ b/packages/grafana-data/src/dataframe/index.ts
@@ -0,0 +1,5 @@
+export * from './DataFrameView';
+export * from './FieldCache';
+export * from './CircularDataFrame';
+export * from './MutableDataFrame';
+export * from './processDataFrame';
diff --git a/packages/grafana-data/src/utils/processDataFrame.test.ts b/packages/grafana-data/src/dataframe/processDataFrame.test.ts
similarity index 98%
rename from packages/grafana-data/src/utils/processDataFrame.test.ts
rename to packages/grafana-data/src/dataframe/processDataFrame.test.ts
index a71dff6e9ca09..adf10690ec5a1 100644
--- a/packages/grafana-data/src/utils/processDataFrame.test.ts
+++ b/packages/grafana-data/src/dataframe/processDataFrame.test.ts
@@ -8,8 +8,8 @@ import {
sortDataFrame,
} from './processDataFrame';
import { FieldType, TimeSeries, TableData, DataFrameDTO } from '../types/index';
-import { dateTime } from './moment_wrapper';
-import { MutableDataFrame } from './dataFrameHelper';
+import { dateTime } from '../datetime/moment_wrapper';
+import { MutableDataFrame } from './MutableDataFrame';
describe('toDataFrame', () => {
it('converts timeseries to series', () => {
diff --git a/packages/grafana-data/src/utils/processDataFrame.ts b/packages/grafana-data/src/dataframe/processDataFrame.ts
similarity index 97%
rename from packages/grafana-data/src/utils/processDataFrame.ts
rename to packages/grafana-data/src/dataframe/processDataFrame.ts
index e88bb54ddecc6..6221bb4e658bd 100644
--- a/packages/grafana-data/src/utils/processDataFrame.ts
+++ b/packages/grafana-data/src/dataframe/processDataFrame.ts
@@ -17,10 +17,11 @@ import {
FieldDTO,
DataFrameDTO,
} from '../types/index';
-import { isDateTime } from './moment_wrapper';
-import { ArrayVector, SortedVector } from './vector';
-import { MutableDataFrame } from './dataFrameHelper';
-import { deprecationWarning } from './deprecationWarning';
+import { isDateTime } from '../datetime/moment_wrapper';
+import { deprecationWarning } from '../utils/deprecationWarning';
+import { ArrayVector } from '../vector/ArrayVector';
+import { MutableDataFrame } from './MutableDataFrame';
+import { SortedVector } from '../vector/SortedVector';
function convertTableToDataFrame(table: TableData): DataFrame {
const fields = table.columns.map(c => {
diff --git a/packages/grafana-data/src/utils/datemath.test.ts b/packages/grafana-data/src/datetime/datemath.test.ts
similarity index 98%
rename from packages/grafana-data/src/utils/datemath.test.ts
rename to packages/grafana-data/src/datetime/datemath.test.ts
index 6ffd9194d7f38..3443eea2d1729 100644
--- a/packages/grafana-data/src/utils/datemath.test.ts
+++ b/packages/grafana-data/src/datetime/datemath.test.ts
@@ -2,7 +2,7 @@ import sinon, { SinonFakeTimers } from 'sinon';
import each from 'lodash/each';
import * as dateMath from './datemath';
-import { dateTime, DurationUnit, DateTime } from '../utils/moment_wrapper';
+import { dateTime, DurationUnit, DateTime } from './moment_wrapper';
describe('DateMath', () => {
const spans: DurationUnit[] = ['s', 'm', 'h', 'd', 'w', 'M', 'y'];
diff --git a/packages/grafana-data/src/utils/datemath.ts b/packages/grafana-data/src/datetime/datemath.ts
similarity index 99%
rename from packages/grafana-data/src/utils/datemath.ts
rename to packages/grafana-data/src/datetime/datemath.ts
index 47fe47282c3e9..29747e15bc722 100644
--- a/packages/grafana-data/src/utils/datemath.ts
+++ b/packages/grafana-data/src/datetime/datemath.ts
@@ -1,7 +1,7 @@
import includes from 'lodash/includes';
import isDate from 'lodash/isDate';
import { DateTime, dateTime, dateTimeForTimeZone, ISO_8601, isDateTime, DurationUnit } from './moment_wrapper';
-import { TimeZone } from '../types';
+import { TimeZone } from '../types/index';
const units: DurationUnit[] = ['y', 'M', 'w', 'd', 'h', 'm', 's'];
diff --git a/packages/grafana-data/src/datetime/index.ts b/packages/grafana-data/src/datetime/index.ts
new file mode 100644
index 0000000000000..bbaa4ebd95cb0
--- /dev/null
+++ b/packages/grafana-data/src/datetime/index.ts
@@ -0,0 +1,5 @@
+// Names are too general to export globally
+import * as dateMath from './datemath';
+import * as rangeUtil from './rangeutil';
+export * from './moment_wrapper';
+export { dateMath, rangeUtil };
diff --git a/packages/grafana-data/src/utils/moment_wrapper.ts b/packages/grafana-data/src/datetime/moment_wrapper.ts
similarity index 100%
rename from packages/grafana-data/src/utils/moment_wrapper.ts
rename to packages/grafana-data/src/datetime/moment_wrapper.ts
diff --git a/packages/grafana-data/src/utils/rangeutil.ts b/packages/grafana-data/src/datetime/rangeutil.ts
similarity index 98%
rename from packages/grafana-data/src/utils/rangeutil.ts
rename to packages/grafana-data/src/datetime/rangeutil.ts
index d8eb04ffd07d3..fcf55b37f4df0 100644
--- a/packages/grafana-data/src/utils/rangeutil.ts
+++ b/packages/grafana-data/src/datetime/rangeutil.ts
@@ -4,7 +4,7 @@ import groupBy from 'lodash/groupBy';
import { RawTimeRange } from '../types/time';
import * as dateMath from './datemath';
-import { isDateTime, DateTime } from '../utils/moment_wrapper';
+import { isDateTime, DateTime } from './moment_wrapper';
const spans: { [key: string]: { display: string; section?: number } } = {
s: { display: 'second' },
diff --git a/packages/grafana-data/src/index.ts b/packages/grafana-data/src/index.ts
index 53f3ef7101117..c5f3add1d5b57 100644
--- a/packages/grafana-data/src/index.ts
+++ b/packages/grafana-data/src/index.ts
@@ -1,2 +1,7 @@
-export * from './utils/index';
-export * from './types/index';
+export * from './utils';
+export * from './types';
+export * from './vector';
+export * from './dataframe';
+export * from './transformations';
+export * from './datetime';
+export * from './text';
diff --git a/packages/grafana-data/src/text/index.ts b/packages/grafana-data/src/text/index.ts
new file mode 100644
index 0000000000000..b59a1d992d5cf
--- /dev/null
+++ b/packages/grafana-data/src/text/index.ts
@@ -0,0 +1,3 @@
+export * from './string';
+export * from './markdown';
+export * from './text';
diff --git a/packages/grafana-data/src/utils/markdown.test.ts b/packages/grafana-data/src/text/markdown.test.ts
similarity index 100%
rename from packages/grafana-data/src/utils/markdown.test.ts
rename to packages/grafana-data/src/text/markdown.test.ts
diff --git a/packages/grafana-data/src/utils/markdown.ts b/packages/grafana-data/src/text/markdown.ts
similarity index 100%
rename from packages/grafana-data/src/utils/markdown.ts
rename to packages/grafana-data/src/text/markdown.ts
diff --git a/packages/grafana-data/src/utils/string.test.ts b/packages/grafana-data/src/text/string.test.ts
similarity index 100%
rename from packages/grafana-data/src/utils/string.test.ts
rename to packages/grafana-data/src/text/string.test.ts
diff --git a/packages/grafana-data/src/utils/string.ts b/packages/grafana-data/src/text/string.ts
similarity index 100%
rename from packages/grafana-data/src/utils/string.ts
rename to packages/grafana-data/src/text/string.ts
diff --git a/packages/grafana-data/src/utils/text.test.ts b/packages/grafana-data/src/text/text.test.ts
similarity index 100%
rename from packages/grafana-data/src/utils/text.test.ts
rename to packages/grafana-data/src/text/text.test.ts
diff --git a/packages/grafana-data/src/utils/text.ts b/packages/grafana-data/src/text/text.ts
similarity index 100%
rename from packages/grafana-data/src/utils/text.ts
rename to packages/grafana-data/src/text/text.ts
diff --git a/packages/grafana-data/src/utils/fieldReducer.test.ts b/packages/grafana-data/src/transformations/fieldReducer.test.ts
similarity index 96%
rename from packages/grafana-data/src/utils/fieldReducer.test.ts
rename to packages/grafana-data/src/transformations/fieldReducer.test.ts
index c4bb1f1e41498..0dfc1d349a1ed 100644
--- a/packages/grafana-data/src/utils/fieldReducer.test.ts
+++ b/packages/grafana-data/src/transformations/fieldReducer.test.ts
@@ -3,9 +3,9 @@ import difference from 'lodash/difference';
import { fieldReducers, ReducerID, reduceField } from './fieldReducer';
import { Field, FieldType } from '../types/index';
-import { MutableDataFrame } from './dataFrameHelper';
-import { ArrayVector } from './vector';
-import { guessFieldTypeFromValue } from './processDataFrame';
+import { guessFieldTypeFromValue } from '../dataframe/processDataFrame';
+import { MutableDataFrame } from '../dataframe/MutableDataFrame';
+import { ArrayVector } from '../vector/ArrayVector';
/**
* Run a reducer and get back the value
diff --git a/packages/grafana-data/src/utils/fieldReducer.ts b/packages/grafana-data/src/transformations/fieldReducer.ts
similarity index 98%
rename from packages/grafana-data/src/utils/fieldReducer.ts
rename to packages/grafana-data/src/transformations/fieldReducer.ts
index 7655a735e3df3..83714374e482c 100644
--- a/packages/grafana-data/src/utils/fieldReducer.ts
+++ b/packages/grafana-data/src/transformations/fieldReducer.ts
@@ -1,8 +1,8 @@
// Libraries
import isNumber from 'lodash/isNumber';
-import { NullValueMode, Field } from '../types';
-import { Registry, RegistryItem } from './registry';
+import { NullValueMode, Field } from '../types/index';
+import { Registry, RegistryItem } from '../utils/Registry';
export enum ReducerID {
sum = 'sum',
diff --git a/packages/grafana-data/src/transformations/index.ts b/packages/grafana-data/src/transformations/index.ts
new file mode 100644
index 0000000000000..798dabce10b12
--- /dev/null
+++ b/packages/grafana-data/src/transformations/index.ts
@@ -0,0 +1,7 @@
+export * from './matchers/ids';
+export * from './transformers/ids';
+export * from './matchers';
+export * from './transformers';
+export * from './fieldReducer';
+export { FilterFieldsByNameTransformerOptions } from './transformers/filterByName';
+export { ReduceTransformerOptions } from './transformers/reduce';
diff --git a/packages/grafana-data/src/utils/matchers/matchers.ts b/packages/grafana-data/src/transformations/matchers.ts
similarity index 55%
rename from packages/grafana-data/src/utils/matchers/matchers.ts
rename to packages/grafana-data/src/transformations/matchers.ts
index d5b45ff771a11..62f127f7518be 100644
--- a/packages/grafana-data/src/utils/matchers/matchers.ts
+++ b/packages/grafana-data/src/transformations/matchers.ts
@@ -1,27 +1,16 @@
-import { Field, DataFrame } from '../../types/dataFrame';
-import { Registry, RegistryItemWithOptions } from '../registry';
-
-export type FieldMatcher = (field: Field) => boolean;
-export type FrameMatcher = (frame: DataFrame) => boolean;
-
-export interface FieldMatcherInfo extends RegistryItemWithOptions {
- get: (options: TOptions) => FieldMatcher;
-}
-
-export interface FrameMatcherInfo extends RegistryItemWithOptions {
- get: (options: TOptions) => FrameMatcher;
-}
-
-export interface MatcherConfig {
- id: string;
- options?: TOptions;
-}
-
// Load the Buildtin matchers
-import { getFieldPredicateMatchers, getFramePredicateMatchers } from './predicates';
-import { getFieldNameMatchers, getFrameNameMatchers } from './nameMatcher';
-import { getFieldTypeMatchers } from './fieldTypeMatcher';
-import { getRefIdMatchers } from './refIdMatcher';
+import { getFieldPredicateMatchers, getFramePredicateMatchers } from './matchers/predicates';
+import { getFieldNameMatchers, getFrameNameMatchers } from './matchers/nameMatcher';
+import { getFieldTypeMatchers } from './matchers/fieldTypeMatcher';
+import { getRefIdMatchers } from './matchers/refIdMatcher';
+import {
+ FieldMatcherInfo,
+ MatcherConfig,
+ FrameMatcherInfo,
+ FieldMatcher,
+ FrameMatcher,
+} from '../types/transformations';
+import { Registry } from '../utils/Registry';
export const fieldMatchers = new Registry(() => {
return [
diff --git a/packages/grafana-data/src/utils/matchers/fieldTypeMatcher.test.ts b/packages/grafana-data/src/transformations/matchers/fieldTypeMatcher.test.ts
similarity index 84%
rename from packages/grafana-data/src/utils/matchers/fieldTypeMatcher.test.ts
rename to packages/grafana-data/src/transformations/matchers/fieldTypeMatcher.test.ts
index 99884f3d60c1d..cf8f7006e3395 100644
--- a/packages/grafana-data/src/utils/matchers/fieldTypeMatcher.test.ts
+++ b/packages/grafana-data/src/transformations/matchers/fieldTypeMatcher.test.ts
@@ -1,7 +1,7 @@
import { FieldType } from '../../types/dataFrame';
-import { fieldMatchers } from './matchers';
+import { fieldMatchers } from '../matchers';
import { FieldMatcherID } from './ids';
-import { toDataFrame } from '../processDataFrame';
+import { toDataFrame } from '../../dataframe/processDataFrame';
export const simpleSeriesWithTypes = toDataFrame({
fields: [
diff --git a/packages/grafana-data/src/utils/matchers/fieldTypeMatcher.ts b/packages/grafana-data/src/transformations/matchers/fieldTypeMatcher.ts
similarity index 95%
rename from packages/grafana-data/src/utils/matchers/fieldTypeMatcher.ts
rename to packages/grafana-data/src/transformations/matchers/fieldTypeMatcher.ts
index 385bcefca44fe..38dc9c5ef0ac5 100644
--- a/packages/grafana-data/src/utils/matchers/fieldTypeMatcher.ts
+++ b/packages/grafana-data/src/transformations/matchers/fieldTypeMatcher.ts
@@ -1,6 +1,6 @@
import { Field, FieldType } from '../../types/dataFrame';
-import { FieldMatcherInfo } from './matchers';
import { FieldMatcherID } from './ids';
+import { FieldMatcherInfo } from '../../types/transformations';
// General Field matcher
const fieldTypeMacher: FieldMatcherInfo = {
diff --git a/packages/grafana-data/src/utils/matchers/ids.ts b/packages/grafana-data/src/transformations/matchers/ids.ts
similarity index 100%
rename from packages/grafana-data/src/utils/matchers/ids.ts
rename to packages/grafana-data/src/transformations/matchers/ids.ts
diff --git a/packages/grafana-data/src/utils/matchers/matchers.test.ts b/packages/grafana-data/src/transformations/matchers/matchers.test.ts
similarity index 85%
rename from packages/grafana-data/src/utils/matchers/matchers.test.ts
rename to packages/grafana-data/src/transformations/matchers/matchers.test.ts
index 0faeabb14aebb..81ea76b2b757a 100644
--- a/packages/grafana-data/src/utils/matchers/matchers.test.ts
+++ b/packages/grafana-data/src/transformations/matchers/matchers.test.ts
@@ -1,4 +1,4 @@
-import { fieldMatchers } from './matchers';
+import { fieldMatchers } from '../matchers';
import { FieldMatcherID } from './ids';
describe('Matchers', () => {
diff --git a/packages/grafana-data/src/utils/matchers/nameMatcher.test.ts b/packages/grafana-data/src/transformations/matchers/nameMatcher.test.ts
similarity index 92%
rename from packages/grafana-data/src/utils/matchers/nameMatcher.test.ts
rename to packages/grafana-data/src/transformations/matchers/nameMatcher.test.ts
index 7f2880ff0f631..0f3df71088f87 100644
--- a/packages/grafana-data/src/utils/matchers/nameMatcher.test.ts
+++ b/packages/grafana-data/src/transformations/matchers/nameMatcher.test.ts
@@ -1,6 +1,6 @@
-import { getFieldMatcher } from './matchers';
+import { getFieldMatcher } from '../matchers';
import { FieldMatcherID } from './ids';
-import { toDataFrame } from '../processDataFrame';
+import { toDataFrame } from '../../dataframe/processDataFrame';
describe('Field Name Matcher', () => {
it('Match all with wildcard regex', () => {
diff --git a/packages/grafana-data/src/utils/matchers/nameMatcher.ts b/packages/grafana-data/src/transformations/matchers/nameMatcher.ts
similarity index 90%
rename from packages/grafana-data/src/utils/matchers/nameMatcher.ts
rename to packages/grafana-data/src/transformations/matchers/nameMatcher.ts
index 626b6a908a8a2..1061c2d804770 100644
--- a/packages/grafana-data/src/utils/matchers/nameMatcher.ts
+++ b/packages/grafana-data/src/transformations/matchers/nameMatcher.ts
@@ -1,7 +1,7 @@
import { Field, DataFrame } from '../../types/dataFrame';
-import { FieldMatcherInfo, FrameMatcherInfo } from './matchers';
import { FieldMatcherID, FrameMatcherID } from './ids';
-import { stringToJsRegex } from '../string';
+import { FieldMatcherInfo, FrameMatcherInfo } from '../../types/transformations';
+import { stringToJsRegex } from '../../text/string';
// General Field matcher
const fieldNameMacher: FieldMatcherInfo = {
diff --git a/packages/grafana-data/src/utils/matchers/predicates.test.ts b/packages/grafana-data/src/transformations/matchers/predicates.test.ts
similarity index 92%
rename from packages/grafana-data/src/utils/matchers/predicates.test.ts
rename to packages/grafana-data/src/transformations/matchers/predicates.test.ts
index 97e95ba3129b7..cb4fd1909c145 100644
--- a/packages/grafana-data/src/utils/matchers/predicates.test.ts
+++ b/packages/grafana-data/src/transformations/matchers/predicates.test.ts
@@ -1,7 +1,8 @@
import { FieldType } from '../../types/dataFrame';
-import { MatcherConfig, fieldMatchers } from './matchers';
+import { fieldMatchers } from '../matchers';
import { simpleSeriesWithTypes } from './fieldTypeMatcher.test';
import { FieldMatcherID, MatcherID } from './ids';
+import { MatcherConfig } from '../../types/transformations';
const matchesNumberConfig: MatcherConfig = {
id: FieldMatcherID.byType,
diff --git a/packages/grafana-data/src/utils/matchers/predicates.ts b/packages/grafana-data/src/transformations/matchers/predicates.ts
similarity index 97%
rename from packages/grafana-data/src/utils/matchers/predicates.ts
rename to packages/grafana-data/src/transformations/matchers/predicates.ts
index 502cceef31117..e122b84ab3c30 100644
--- a/packages/grafana-data/src/utils/matchers/predicates.ts
+++ b/packages/grafana-data/src/transformations/matchers/predicates.ts
@@ -1,14 +1,7 @@
import { Field, DataFrame } from '../../types/dataFrame';
import { MatcherID } from './ids';
-import {
- FrameMatcherInfo,
- FieldMatcherInfo,
- MatcherConfig,
- getFieldMatcher,
- fieldMatchers,
- getFrameMatchers,
- frameMatchers,
-} from './matchers';
+import { getFieldMatcher, fieldMatchers, getFrameMatchers, frameMatchers } from '../matchers';
+import { FieldMatcherInfo, MatcherConfig, FrameMatcherInfo } from '../../types/transformations';
const anyFieldMatcher: FieldMatcherInfo = {
id: MatcherID.anyMatch,
diff --git a/packages/grafana-data/src/utils/matchers/refIdMatcher.ts b/packages/grafana-data/src/transformations/matchers/refIdMatcher.ts
similarity index 89%
rename from packages/grafana-data/src/utils/matchers/refIdMatcher.ts
rename to packages/grafana-data/src/transformations/matchers/refIdMatcher.ts
index 51b0db3af8051..0d525d53dd91c 100644
--- a/packages/grafana-data/src/utils/matchers/refIdMatcher.ts
+++ b/packages/grafana-data/src/transformations/matchers/refIdMatcher.ts
@@ -1,6 +1,6 @@
import { DataFrame } from '../../types/dataFrame';
-import { FrameMatcherInfo } from './matchers';
import { FrameMatcherID } from './ids';
+import { FrameMatcherInfo } from '../../types/transformations';
// General Field matcher
const refIdMacher: FrameMatcherInfo = {
diff --git a/packages/grafana-data/src/utils/transformers/transformers.test.ts b/packages/grafana-data/src/transformations/transformers.test.ts
similarity index 62%
rename from packages/grafana-data/src/utils/transformers/transformers.test.ts
rename to packages/grafana-data/src/transformations/transformers.test.ts
index 81df7478d594c..655587e266790 100644
--- a/packages/grafana-data/src/utils/transformers/transformers.test.ts
+++ b/packages/grafana-data/src/transformations/transformers.test.ts
@@ -1,13 +1,13 @@
-import { DataTransformerID } from './ids';
-import { dataTransformers } from './transformers';
-import { toDataFrame } from '../processDataFrame';
-import { ReducerID } from '../fieldReducer';
-import { DataFrameView } from '../dataFrameView';
+import { DataTransformerID } from './transformers/ids';
+import { transformersRegistry } from './transformers';
+import { toDataFrame } from '../dataframe/processDataFrame';
+import { ReducerID } from './fieldReducer';
+import { DataFrameView } from '../dataframe/DataFrameView';
describe('Transformers', () => {
it('should load all transformeres', () => {
for (const name of Object.keys(DataTransformerID)) {
- const calc = dataTransformers.get(name);
+ const calc = transformersRegistry.get(name);
expect(calc.id).toBe(name);
}
});
@@ -20,7 +20,7 @@ describe('Transformers', () => {
});
it('should use fluent API', () => {
- const results = dataTransformers.reduce([seriesWithValues], {
+ const results = transformersRegistry.reduce([seriesWithValues], {
reducers: [ReducerID.first],
});
expect(results.length).toBe(1);
diff --git a/packages/grafana-data/src/utils/transformers/transformers.ts b/packages/grafana-data/src/transformations/transformers.ts
similarity index 71%
rename from packages/grafana-data/src/utils/transformers/transformers.ts
rename to packages/grafana-data/src/transformations/transformers.ts
index 04cee6e763410..189a76d03abed 100644
--- a/packages/grafana-data/src/utils/transformers/transformers.ts
+++ b/packages/grafana-data/src/transformations/transformers.ts
@@ -1,19 +1,13 @@
-import { DataFrame } from '../../types/dataFrame';
-import { Registry, RegistryItemWithOptions } from '../registry';
-
-/**
- * Immutable data transformation
- */
-export type DataTransformer = (data: DataFrame[]) => DataFrame[];
-
-export interface DataTransformerInfo extends RegistryItemWithOptions {
- transformer: (options: TOptions) => DataTransformer;
-}
+import { DataFrame } from '../types/dataFrame';
+import { Registry } from '../utils/Registry';
+// Initalize the Registry
-export interface DataTransformerConfig {
- id: string;
- options: TOptions;
-}
+import { appendTransformer, AppendOptions } from './transformers/append';
+import { reduceTransformer, ReduceTransformerOptions } from './transformers/reduce';
+import { filterFieldsTransformer, filterFramesTransformer } from './transformers/filter';
+import { filterFieldsByNameTransformer, FilterFieldsByNameTransformerOptions } from './transformers/filterByName';
+import { noopTransformer } from './transformers/noop';
+import { DataTransformerInfo, DataTransformerConfig } from '../types/transformations';
/**
* Apply configured transformations to the input data
@@ -21,7 +15,7 @@ export interface DataTransformerConfig {
export function transformDataFrame(options: DataTransformerConfig[], data: DataFrame[]): DataFrame[] {
let processed = data;
for (const config of options) {
- const info = dataTransformers.get(config.id);
+ const info = transformersRegistry.get(config.id);
const transformer = info.transformer(config.options);
const after = transformer(processed);
@@ -43,14 +37,6 @@ export function transformDataFrame(options: DataTransformerConfig[], data: DataF
return processed;
}
-// Initalize the Registry
-
-import { appendTransformer, AppendOptions } from './append';
-import { reduceTransformer, ReduceTransformerOptions } from './reduce';
-import { filterFieldsTransformer, filterFramesTransformer } from './filter';
-import { filterFieldsByNameTransformer, FilterFieldsByNameTransformerOptions } from './filterByName';
-import { noopTransformer } from './noop';
-
/**
* Registry of transformation options that can be driven by
* stored configuration files.
@@ -73,7 +59,7 @@ class TransformerRegistry extends Registry {
}
}
-export const dataTransformers = new TransformerRegistry(() => [
+export const transformersRegistry = new TransformerRegistry(() => [
noopTransformer,
filterFieldsTransformer,
filterFieldsByNameTransformer,
diff --git a/packages/grafana-data/src/utils/transformers/__snapshots__/reduce.test.ts.snap b/packages/grafana-data/src/transformations/transformers/__snapshots__/reduce.test.ts.snap
similarity index 100%
rename from packages/grafana-data/src/utils/transformers/__snapshots__/reduce.test.ts.snap
rename to packages/grafana-data/src/transformations/transformers/__snapshots__/reduce.test.ts.snap
diff --git a/packages/grafana-data/src/utils/transformers/append.test.ts b/packages/grafana-data/src/transformations/transformers/append.test.ts
similarity index 79%
rename from packages/grafana-data/src/utils/transformers/append.test.ts
rename to packages/grafana-data/src/transformations/transformers/append.test.ts
index 4881c714f28e3..6d92627dccddc 100644
--- a/packages/grafana-data/src/utils/transformers/append.test.ts
+++ b/packages/grafana-data/src/transformations/transformers/append.test.ts
@@ -1,6 +1,7 @@
-import { transformDataFrame, dataTransformers } from './transformers';
import { DataTransformerID } from './ids';
-import { toDataFrame } from '../processDataFrame';
+import { toDataFrame } from '../../dataframe/processDataFrame';
+import { transformDataFrame } from '../transformers';
+import { transformersRegistry } from '../transformers';
const seriesAB = toDataFrame({
columns: [{ text: 'A' }, { text: 'B' }],
@@ -24,7 +25,7 @@ describe('Append Transformer', () => {
id: DataTransformerID.append,
options: {},
};
- const x = dataTransformers.get(DataTransformerID.append);
+ const x = transformersRegistry.get(DataTransformerID.append);
expect(x.id).toBe(cfg.id);
const processed = transformDataFrame([cfg], [seriesAB, seriesBC])[0];
diff --git a/packages/grafana-data/src/utils/transformers/append.ts b/packages/grafana-data/src/transformations/transformers/append.ts
similarity index 91%
rename from packages/grafana-data/src/utils/transformers/append.ts
rename to packages/grafana-data/src/transformations/transformers/append.ts
index b982cdb147837..952f006bbe2b3 100644
--- a/packages/grafana-data/src/utils/transformers/append.ts
+++ b/packages/grafana-data/src/transformations/transformers/append.ts
@@ -1,7 +1,7 @@
-import { DataTransformerInfo } from './transformers';
import { DataFrame } from '../../types/dataFrame';
import { DataTransformerID } from './ids';
-import { MutableDataFrame } from '../dataFrameHelper';
+import { MutableDataFrame } from '../../dataframe/MutableDataFrame';
+import { DataTransformerInfo } from '../../types/transformations';
export interface AppendOptions {}
diff --git a/packages/grafana-data/src/utils/transformers/filter.test.ts b/packages/grafana-data/src/transformations/transformers/filter.test.ts
similarity index 87%
rename from packages/grafana-data/src/utils/transformers/filter.test.ts
rename to packages/grafana-data/src/transformations/transformers/filter.test.ts
index 28b3c11f6f700..af8846674bc2d 100644
--- a/packages/grafana-data/src/utils/transformers/filter.test.ts
+++ b/packages/grafana-data/src/transformations/transformers/filter.test.ts
@@ -1,8 +1,8 @@
import { FieldType } from '../../types/dataFrame';
-import { FieldMatcherID } from '../matchers/ids';
-import { transformDataFrame } from './transformers';
import { DataTransformerID } from './ids';
-import { toDataFrame } from '../processDataFrame';
+import { toDataFrame } from '../../dataframe/processDataFrame';
+import { FieldMatcherID } from '../matchers/ids';
+import { transformDataFrame } from '../transformers';
export const simpleSeriesWithTypes = toDataFrame({
fields: [
diff --git a/packages/grafana-data/src/utils/transformers/filter.ts b/packages/grafana-data/src/transformations/transformers/filter.ts
similarity index 95%
rename from packages/grafana-data/src/utils/transformers/filter.ts
rename to packages/grafana-data/src/transformations/transformers/filter.ts
index 7837c03d233fa..fcce7766c872a 100644
--- a/packages/grafana-data/src/utils/transformers/filter.ts
+++ b/packages/grafana-data/src/transformations/transformers/filter.ts
@@ -1,9 +1,9 @@
-import { DataTransformerInfo } from './transformers';
import { noopTransformer } from './noop';
import { DataFrame, Field } from '../../types/dataFrame';
-import { FieldMatcherID } from '../matchers/ids';
import { DataTransformerID } from './ids';
-import { MatcherConfig, getFieldMatcher, getFrameMatchers } from '../matchers/matchers';
+import { DataTransformerInfo, MatcherConfig } from '../../types/transformations';
+import { FieldMatcherID } from '../matchers/ids';
+import { getFieldMatcher, getFrameMatchers } from '../matchers';
export interface FilterOptions {
include?: MatcherConfig;
diff --git a/packages/grafana-data/src/utils/transformers/filterByName.test.ts b/packages/grafana-data/src/transformations/transformers/filterByName.test.ts
similarity index 91%
rename from packages/grafana-data/src/utils/transformers/filterByName.test.ts
rename to packages/grafana-data/src/transformations/transformers/filterByName.test.ts
index 7552506541287..ac764da977de6 100644
--- a/packages/grafana-data/src/utils/transformers/filterByName.test.ts
+++ b/packages/grafana-data/src/transformations/transformers/filterByName.test.ts
@@ -1,6 +1,7 @@
-import { toDataFrame, transformDataFrame } from '../index';
-import { FieldType } from '../../index';
import { DataTransformerID } from './ids';
+import { transformDataFrame } from '../transformers';
+import { toDataFrame } from '../../dataframe/processDataFrame';
+import { FieldType } from '../../types/dataFrame';
export const seriesWithNamesToMatch = toDataFrame({
fields: [
diff --git a/packages/grafana-data/src/utils/transformers/filterByName.ts b/packages/grafana-data/src/transformations/transformers/filterByName.ts
similarity index 94%
rename from packages/grafana-data/src/utils/transformers/filterByName.ts
rename to packages/grafana-data/src/transformations/transformers/filterByName.ts
index 1d72116dc72b4..7ece827065c46 100644
--- a/packages/grafana-data/src/utils/transformers/filterByName.ts
+++ b/packages/grafana-data/src/transformations/transformers/filterByName.ts
@@ -1,7 +1,7 @@
-import { DataTransformerInfo } from './transformers';
-import { FieldMatcherID } from '../matchers/ids';
import { DataTransformerID } from './ids';
import { filterFieldsTransformer, FilterOptions } from './filter';
+import { DataTransformerInfo } from '../../types/transformations';
+import { FieldMatcherID } from '../matchers/ids';
export interface FilterFieldsByNameTransformerOptions {
include?: string;
diff --git a/packages/grafana-data/src/utils/transformers/ids.ts b/packages/grafana-data/src/transformations/transformers/ids.ts
similarity index 100%
rename from packages/grafana-data/src/utils/transformers/ids.ts
rename to packages/grafana-data/src/transformations/transformers/ids.ts
diff --git a/packages/grafana-data/src/utils/transformers/noop.ts b/packages/grafana-data/src/transformations/transformers/noop.ts
similarity index 90%
rename from packages/grafana-data/src/utils/transformers/noop.ts
rename to packages/grafana-data/src/transformations/transformers/noop.ts
index 96bded6b89a34..7feb0b853d6f5 100644
--- a/packages/grafana-data/src/utils/transformers/noop.ts
+++ b/packages/grafana-data/src/transformations/transformers/noop.ts
@@ -1,6 +1,6 @@
-import { DataTransformerInfo } from './transformers';
import { DataTransformerID } from './ids';
import { DataFrame } from '../../types/dataFrame';
+import { DataTransformerInfo } from '../../types/transformations';
export interface NoopTransformerOptions {
include?: string;
diff --git a/packages/grafana-data/src/utils/transformers/reduce.test.ts b/packages/grafana-data/src/transformations/transformers/reduce.test.ts
similarity index 83%
rename from packages/grafana-data/src/utils/transformers/reduce.test.ts
rename to packages/grafana-data/src/transformations/transformers/reduce.test.ts
index ed0ce16b6f71e..244f87dff8c7a 100644
--- a/packages/grafana-data/src/utils/transformers/reduce.test.ts
+++ b/packages/grafana-data/src/transformations/transformers/reduce.test.ts
@@ -1,7 +1,7 @@
-import { transformDataFrame } from './transformers';
import { ReducerID } from '../fieldReducer';
import { DataTransformerID } from './ids';
-import { toDataFrame, toDataFrameDTO } from '../processDataFrame';
+import { toDataFrame, toDataFrameDTO } from '../../dataframe/processDataFrame';
+import { transformDataFrame } from '../transformers';
const seriesWithValues = toDataFrame({
fields: [
diff --git a/packages/grafana-data/src/utils/transformers/reduce.ts b/packages/grafana-data/src/transformations/transformers/reduce.ts
similarity index 89%
rename from packages/grafana-data/src/utils/transformers/reduce.ts
rename to packages/grafana-data/src/transformations/transformers/reduce.ts
index a30a9c0ef1233..ca61bc93c2ea9 100644
--- a/packages/grafana-data/src/utils/transformers/reduce.ts
+++ b/packages/grafana-data/src/transformations/transformers/reduce.ts
@@ -1,12 +1,12 @@
-import { DataTransformerInfo } from './transformers';
-import { DataFrame, FieldType, Field } from '../../types/dataFrame';
-import { MatcherConfig, getFieldMatcher } from '../matchers/matchers';
-import { alwaysFieldMatcher } from '../matchers/predicates';
import { DataTransformerID } from './ids';
+import { MatcherConfig, DataTransformerInfo } from '../../types/transformations';
import { ReducerID, fieldReducers, reduceField } from '../fieldReducer';
+import { alwaysFieldMatcher } from '../matchers/predicates';
+import { DataFrame, Field, FieldType } from '../../types/dataFrame';
+import { ArrayVector } from '../../vector/ArrayVector';
import { KeyValue } from '../../types/data';
-import { ArrayVector } from '../vector';
-import { guessFieldTypeForField } from '../processDataFrame';
+import { guessFieldTypeForField } from '../../dataframe/processDataFrame';
+import { getFieldMatcher } from '../matchers';
export interface ReduceTransformerOptions {
reducers: ReducerID[];
diff --git a/packages/grafana-data/src/types/dataFrame.ts b/packages/grafana-data/src/types/dataFrame.ts
index add607d0a53b7..03d965160ae8f 100644
--- a/packages/grafana-data/src/types/dataFrame.ts
+++ b/packages/grafana-data/src/types/dataFrame.ts
@@ -1,9 +1,10 @@
import { Threshold } from './threshold';
import { ValueMapping } from './valueMapping';
import { QueryResultBase, Labels, NullValueMode } from './data';
-import { FieldCalcs } from '../utils/index';
import { DisplayProcessor } from './displayValue';
import { DataLink } from './dataLink';
+import { Vector } from './vector';
+import { FieldCalcs } from '../transformations/fieldReducer';
export enum FieldType {
time = 'time', // or date
@@ -44,25 +45,6 @@ export interface FieldConfig {
noValue?: string;
}
-export interface Vector {
- length: number;
-
- /**
- * Access the value by index (Like an array)
- */
- get(index: number): T;
-
- /**
- * Get the resutls as an array.
- */
- toArray(): T[];
-
- /**
- * Return the values as a simple array for json serialization
- */
- toJSON(): any; // same results as toArray()
-}
-
export interface Field> {
name: string; // The column name
type: FieldType;
diff --git a/packages/grafana-data/src/types/index.ts b/packages/grafana-data/src/types/index.ts
index 08340b2d01047..fd8d362074ab9 100644
--- a/packages/grafana-data/src/types/index.ts
+++ b/packages/grafana-data/src/types/index.ts
@@ -11,3 +11,4 @@ export * from './valueMapping';
export * from './displayValue';
export * from './graph';
export * from './ScopedVars';
+export * from './transformations';
diff --git a/packages/grafana-data/src/types/time.ts b/packages/grafana-data/src/types/time.ts
index ae8137277f386..0eb2c51929d1f 100644
--- a/packages/grafana-data/src/types/time.ts
+++ b/packages/grafana-data/src/types/time.ts
@@ -1,4 +1,4 @@
-import { DateTime } from '../utils/moment_wrapper';
+import { DateTime } from '../datetime/moment_wrapper';
export interface RawTimeRange {
from: DateTime | string;
diff --git a/packages/grafana-data/src/types/transformations.ts b/packages/grafana-data/src/types/transformations.ts
new file mode 100644
index 0000000000000..6fab55410f537
--- /dev/null
+++ b/packages/grafana-data/src/types/transformations.ts
@@ -0,0 +1,32 @@
+import { DataFrame, Field } from './dataFrame';
+import { RegistryItemWithOptions } from '../utils/Registry';
+
+/**
+ * Immutable data transformation
+ */
+export type DataTransformer = (data: DataFrame[]) => DataFrame[];
+
+export interface DataTransformerInfo extends RegistryItemWithOptions {
+ transformer: (options: TOptions) => DataTransformer;
+}
+
+export interface DataTransformerConfig {
+ id: string;
+ options: TOptions;
+}
+
+export type FieldMatcher = (field: Field) => boolean;
+export type FrameMatcher = (frame: DataFrame) => boolean;
+
+export interface FieldMatcherInfo extends RegistryItemWithOptions {
+ get: (options: TOptions) => FieldMatcher;
+}
+
+export interface FrameMatcherInfo extends RegistryItemWithOptions {
+ get: (options: TOptions) => FrameMatcher;
+}
+
+export interface MatcherConfig {
+ id: string;
+ options?: TOptions;
+}
diff --git a/packages/grafana-data/src/types/vector.ts b/packages/grafana-data/src/types/vector.ts
new file mode 100644
index 0000000000000..7e667d725a9f2
--- /dev/null
+++ b/packages/grafana-data/src/types/vector.ts
@@ -0,0 +1,40 @@
+export interface Vector {
+ length: number;
+
+ /**
+ * Access the value by index (Like an array)
+ */
+ get(index: number): T;
+
+ /**
+ * Get the resutls as an array.
+ */
+ toArray(): T[];
+
+ /**
+ * Return the values as a simple array for json serialization
+ */
+ toJSON(): any; // same results as toArray()
+}
+
+/**
+ * Apache arrow vectors are Read/Write
+ */
+export interface ReadWriteVector extends Vector {
+ set: (index: number, value: T) => void;
+}
+
+/**
+ * Vector with standard manipulation functions
+ */
+export interface MutableVector extends ReadWriteVector {
+ /**
+ * Adds the value to the vector
+ */
+ add: (value: T) => void;
+
+ /**
+ * modifies the vector so it is now the oposite order
+ */
+ reverse: () => void;
+}
diff --git a/packages/grafana-data/src/utils/registry.ts b/packages/grafana-data/src/utils/Registry.ts
similarity index 100%
rename from packages/grafana-data/src/utils/registry.ts
rename to packages/grafana-data/src/utils/Registry.ts
diff --git a/packages/grafana-data/src/utils/csv.test.ts b/packages/grafana-data/src/utils/csv.test.ts
index 23adebcf467b7..c7ed640bfa9fa 100644
--- a/packages/grafana-data/src/utils/csv.test.ts
+++ b/packages/grafana-data/src/utils/csv.test.ts
@@ -1,9 +1,9 @@
import { readCSV, toCSV, CSVHeaderStyle } from './csv';
-import { getDataFrameRow } from './processDataFrame';
+import { getDataFrameRow } from '../dataframe/processDataFrame';
// Test with local CSV files
import fs from 'fs';
-import { toDataFrameDTO } from './processDataFrame';
+import { toDataFrameDTO } from '../dataframe/processDataFrame';
describe('read csv', () => {
it('should get X and y', () => {
diff --git a/packages/grafana-data/src/utils/csv.ts b/packages/grafana-data/src/utils/csv.ts
index fd3664676e226..3d78716310182 100644
--- a/packages/grafana-data/src/utils/csv.ts
+++ b/packages/grafana-data/src/utils/csv.ts
@@ -5,8 +5,8 @@ import isNumber from 'lodash/isNumber';
// Types
import { DataFrame, Field, FieldType, FieldConfig } from '../types';
-import { guessFieldTypeFromValue } from './processDataFrame';
-import { MutableDataFrame } from './dataFrameHelper';
+import { guessFieldTypeFromValue } from '../dataframe/processDataFrame';
+import { MutableDataFrame } from '../dataframe/MutableDataFrame';
export enum CSVHeaderStyle {
full,
diff --git a/packages/grafana-data/src/utils/fieldParser.ts b/packages/grafana-data/src/utils/fieldParser.ts
new file mode 100644
index 0000000000000..939f4e401f299
--- /dev/null
+++ b/packages/grafana-data/src/utils/fieldParser.ts
@@ -0,0 +1,28 @@
+import { Field, FieldType } from '../types/dataFrame';
+import { guessFieldTypeFromValue } from '../dataframe/processDataFrame';
+
+export function makeFieldParser(value: any, field: Field): (value: string) => any {
+ if (!field.type) {
+ if (field.name === 'time' || field.name === 'Time') {
+ field.type = FieldType.time;
+ } else {
+ field.type = guessFieldTypeFromValue(value);
+ }
+ }
+
+ if (field.type === FieldType.number) {
+ return (value: string) => {
+ return parseFloat(value);
+ };
+ }
+
+ // Will convert anything that starts with "T" to true
+ if (field.type === FieldType.boolean) {
+ return (value: string) => {
+ return !(value[0] === 'F' || value[0] === 'f' || value[0] === '0');
+ };
+ }
+
+ // Just pass the string back
+ return (value: string) => value;
+}
diff --git a/packages/grafana-data/src/utils/index.ts b/packages/grafana-data/src/utils/index.ts
index 2479adafe5892..5f3d7d23ef4b1 100644
--- a/packages/grafana-data/src/utils/index.ts
+++ b/packages/grafana-data/src/utils/index.ts
@@ -1,29 +1,10 @@
-export * from './string';
-export * from './registry';
-export * from './markdown';
-export * from './processDataFrame';
+export * from './Registry';
export * from './deprecationWarning';
export * from './csv';
-export * from './fieldReducer';
export * from './logs';
export * from './labels';
export * from './labels';
export * from './object';
-export * from './moment_wrapper';
export * from './thresholds';
-export * from './text';
-export * from './dataFrameHelper';
-export * from './dataFrameView';
-export * from './vector';
export { getMappedValue } from './valueMappings';
-
-// Names are too general to export globally
-import * as dateMath from './datemath';
-import * as rangeUtil from './rangeutil';
-export { dateMath, rangeUtil };
-
-export * from './matchers/ids';
-export * from './matchers/matchers';
-export * from './transformers/ids';
-export * from './transformers/transformers';
diff --git a/packages/grafana-data/src/utils/logs.ts b/packages/grafana-data/src/utils/logs.ts
index 196a6a28dd734..b14542d57feac 100644
--- a/packages/grafana-data/src/utils/logs.ts
+++ b/packages/grafana-data/src/utils/logs.ts
@@ -2,7 +2,7 @@ import { countBy, chain, map, escapeRegExp } from 'lodash';
import { LogLevel, LogRowModel, LogLabelStatsModel, LogsParser } from '../types/logs';
import { DataFrame, FieldType } from '../types/index';
-import { ArrayVector } from './vector';
+import { ArrayVector } from '../vector/ArrayVector';
const LOGFMT_REGEXP = /(?:^|\s)(\w+)=("[^"]*"|\S+)/;
diff --git a/packages/grafana-data/src/utils/vector.ts b/packages/grafana-data/src/utils/vector.ts
deleted file mode 100644
index 0a625b01b4947..0000000000000
--- a/packages/grafana-data/src/utils/vector.ts
+++ /dev/null
@@ -1,338 +0,0 @@
-import { Vector } from '../types/dataFrame';
-
-export function vectorToArray(v: Vector): T[] {
- const arr: T[] = [];
- for (let i = 0; i < v.length; i++) {
- arr[i] = v.get(i);
- }
- return arr;
-}
-
-/**
- * Apache arrow vectors are Read/Write
- */
-export interface ReadWriteVector extends Vector {
- set: (index: number, value: T) => void;
-}
-
-/**
- * Vector with standard manipulation functions
- */
-export interface MutableVector extends ReadWriteVector {
- /**
- * Adds the value to the vector
- */
- add: (value: T) => void;
-
- /**
- * modifies the vector so it is now the oposite order
- */
- reverse: () => void;
-}
-
-export class ArrayVector implements MutableVector {
- buffer: T[];
-
- constructor(buffer?: T[]) {
- this.buffer = buffer ? buffer : [];
- }
-
- get length() {
- return this.buffer.length;
- }
-
- add(value: T) {
- this.buffer.push(value);
- }
-
- get(index: number): T {
- return this.buffer[index];
- }
-
- set(index: number, value: T) {
- this.buffer[index] = value;
- }
-
- reverse() {
- this.buffer.reverse();
- }
-
- toArray(): T[] {
- return this.buffer;
- }
-
- toJSON(): T[] {
- return this.buffer;
- }
-}
-
-export class ConstantVector implements Vector {
- constructor(private value: T, private len: number) {}
-
- get length() {
- return this.len;
- }
-
- get(index: number): T {
- return this.value;
- }
-
- toArray(): T[] {
- const arr = new Array(this.length);
- return arr.fill(this.value);
- }
-
- toJSON(): T[] {
- return this.toArray();
- }
-}
-
-export class ScaledVector implements Vector {
- constructor(private source: Vector, private scale: number) {}
-
- get length(): number {
- return this.source.length;
- }
-
- get(index: number): number {
- return this.source.get(index) * this.scale;
- }
-
- toArray(): number[] {
- return vectorToArray(this);
- }
-
- toJSON(): number[] {
- return vectorToArray(this);
- }
-}
-
-/**
- * Values are returned in the order defined by the input parameter
- */
-export class SortedVector implements Vector {
- constructor(private source: Vector, private order: number[]) {}
-
- get length(): number {
- return this.source.length;
- }
-
- get(index: number): T {
- return this.source.get(this.order[index]);
- }
-
- toArray(): T[] {
- return vectorToArray(this);
- }
-
- toJSON(): T[] {
- return vectorToArray(this);
- }
-}
-
-interface CircularOptions {
- buffer?: T[];
- append?: 'head' | 'tail';
- capacity?: number;
-}
-
-/**
- * Circular vector uses a single buffer to capture a stream of values
- * overwriting the oldest value on add.
- *
- * This supports addting to the 'head' or 'tail' and will grow the buffer
- * to match a configured capacity.
- */
-export class CircularVector implements MutableVector {
- private buffer: T[];
- private index: number;
- private capacity: number;
- private tail: boolean;
-
- constructor(options: CircularOptions) {
- this.buffer = options.buffer || [];
- this.capacity = this.buffer.length;
- this.tail = 'head' !== options.append;
- this.index = 0;
-
- this.add = this.getAddFunction();
- if (options.capacity) {
- this.setCapacity(options.capacity);
- }
- }
-
- /**
- * This gets the appropriate add function depending on the buffer state:
- * * head vs tail
- * * growing buffer vs overwriting values
- */
- private getAddFunction() {
- // When we are not at capacity, it should actually modify the buffer
- if (this.capacity > this.buffer.length) {
- if (this.tail) {
- return (value: T) => {
- this.buffer.push(value);
- if (this.buffer.length >= this.capacity) {
- this.add = this.getAddFunction();
- }
- };
- } else {
- return (value: T) => {
- this.buffer.unshift(value);
- if (this.buffer.length >= this.capacity) {
- this.add = this.getAddFunction();
- }
- };
- }
- }
-
- if (this.tail) {
- return (value: T) => {
- this.buffer[this.index] = value;
- this.index = (this.index + 1) % this.buffer.length;
- };
- }
-
- // Append values to the head
- return (value: T) => {
- let idx = this.index - 1;
- if (idx < 0) {
- idx = this.buffer.length - 1;
- }
- this.buffer[idx] = value;
- this.index = idx;
- };
- }
-
- setCapacity(v: number) {
- if (this.capacity === v) {
- return;
- }
- // Make a copy so it is in order and new additions can be at the head or tail
- const copy = this.toArray();
- if (v > this.length) {
- this.buffer = copy;
- } else if (v < this.capacity) {
- // Shrink the buffer
- const delta = this.length - v;
- if (this.tail) {
- this.buffer = copy.slice(delta, copy.length); // Keep last items
- } else {
- this.buffer = copy.slice(0, copy.length - delta); // Keep first items
- }
- }
- this.capacity = v;
- this.index = 0;
- this.add = this.getAddFunction();
- }
-
- setAppendMode(mode: 'head' | 'tail') {
- const tail = 'head' !== mode;
- if (tail !== this.tail) {
- this.buffer = this.toArray().reverse();
- this.index = 0;
- this.tail = tail;
- this.add = this.getAddFunction();
- }
- }
-
- reverse() {
- this.buffer.reverse();
- }
-
- /**
- * Add the value to the buffer
- */
- add: (value: T) => void;
-
- get(index: number) {
- return this.buffer[(index + this.index) % this.buffer.length];
- }
-
- set(index: number, value: T) {
- this.buffer[(index + this.index) % this.buffer.length] = value;
- }
-
- get length() {
- return this.buffer.length;
- }
-
- toArray(): T[] {
- return vectorToArray(this);
- }
-
- toJSON(): T[] {
- return vectorToArray(this);
- }
-}
-
-interface AppendedVectorInfo {
- start: number;
- end: number;
- values: Vector;
-}
-
-/**
- * This may be more trouble than it is worth. This trades some computation time for
- * RAM -- rather than allocate a new array the size of all previous arrays, this just
- * points the correct index to their original array values
- */
-export class AppendedVectors implements Vector {
- length = 0;
- source: Array> = new Array>();
-
- constructor(startAt = 0) {
- this.length = startAt;
- }
-
- /**
- * Make the vector look like it is this long
- */
- setLength(length: number) {
- if (length > this.length) {
- // make the vector longer (filling with undefined)
- this.length = length;
- } else if (length < this.length) {
- // make the array shorter
- const sources: Array> = new Array>();
- for (const src of this.source) {
- sources.push(src);
- if (src.end > length) {
- src.end = length;
- break;
- }
- }
- this.source = sources;
- this.length = length;
- }
- }
-
- append(v: Vector): AppendedVectorInfo {
- const info = {
- start: this.length,
- end: this.length + v.length,
- values: v,
- };
- this.length = info.end;
- this.source.push(info);
- return info;
- }
-
- get(index: number): T {
- for (let i = 0; i < this.source.length; i++) {
- const src = this.source[i];
- if (index >= src.start && index < src.end) {
- return src.values.get(index - src.start);
- }
- }
- return (undefined as unknown) as T;
- }
-
- toArray(): T[] {
- return vectorToArray(this);
- }
-
- toJSON(): T[] {
- return vectorToArray(this);
- }
-}
diff --git a/packages/grafana-data/src/vector/AppendedVectors.test.ts b/packages/grafana-data/src/vector/AppendedVectors.test.ts
new file mode 100644
index 0000000000000..9d39b47ce091e
--- /dev/null
+++ b/packages/grafana-data/src/vector/AppendedVectors.test.ts
@@ -0,0 +1,23 @@
+import { ArrayVector } from './ArrayVector';
+import { AppendedVectors } from './AppendedVectors';
+
+describe('Check Appending Vector', () => {
+ it('should transparently join them', () => {
+ const appended = new AppendedVectors();
+ appended.append(new ArrayVector([1, 2, 3]));
+ appended.append(new ArrayVector([4, 5, 6]));
+ appended.append(new ArrayVector([7, 8, 9]));
+ expect(appended.length).toEqual(9);
+
+ appended.setLength(5);
+ expect(appended.length).toEqual(5);
+ appended.append(new ArrayVector(['a', 'b', 'c']));
+ expect(appended.length).toEqual(8);
+ expect(appended.toArray()).toEqual([1, 2, 3, 4, 5, 'a', 'b', 'c']);
+
+ appended.setLength(2);
+ appended.setLength(6);
+ appended.append(new ArrayVector(['x', 'y', 'z']));
+ expect(appended.toArray()).toEqual([1, 2, undefined, undefined, undefined, undefined, 'x', 'y', 'z']);
+ });
+});
diff --git a/packages/grafana-data/src/vector/AppendedVectors.ts b/packages/grafana-data/src/vector/AppendedVectors.ts
new file mode 100644
index 0000000000000..7cfc0d42c4706
--- /dev/null
+++ b/packages/grafana-data/src/vector/AppendedVectors.ts
@@ -0,0 +1,73 @@
+import { Vector } from '../types/vector';
+import { vectorToArray } from './vectorToArray';
+
+interface AppendedVectorInfo {
+ start: number;
+ end: number;
+ values: Vector;
+}
+
+/**
+ * This may be more trouble than it is worth. This trades some computation time for
+ * RAM -- rather than allocate a new array the size of all previous arrays, this just
+ * points the correct index to their original array values
+ */
+export class AppendedVectors implements Vector {
+ length = 0;
+ source: Array> = new Array>();
+
+ constructor(startAt = 0) {
+ this.length = startAt;
+ }
+
+ /**
+ * Make the vector look like it is this long
+ */
+ setLength(length: number) {
+ if (length > this.length) {
+ // make the vector longer (filling with undefined)
+ this.length = length;
+ } else if (length < this.length) {
+ // make the array shorter
+ const sources: Array> = new Array>();
+ for (const src of this.source) {
+ sources.push(src);
+ if (src.end > length) {
+ src.end = length;
+ break;
+ }
+ }
+ this.source = sources;
+ this.length = length;
+ }
+ }
+
+ append(v: Vector): AppendedVectorInfo {
+ const info = {
+ start: this.length,
+ end: this.length + v.length,
+ values: v,
+ };
+ this.length = info.end;
+ this.source.push(info);
+ return info;
+ }
+
+ get(index: number): T {
+ for (let i = 0; i < this.source.length; i++) {
+ const src = this.source[i];
+ if (index >= src.start && index < src.end) {
+ return src.values.get(index - src.start);
+ }
+ }
+ return (undefined as unknown) as T;
+ }
+
+ toArray(): T[] {
+ return vectorToArray(this);
+ }
+
+ toJSON(): T[] {
+ return vectorToArray(this);
+ }
+}
diff --git a/packages/grafana-data/src/vector/ArrayVector.ts b/packages/grafana-data/src/vector/ArrayVector.ts
new file mode 100644
index 0000000000000..63c614f1cc4d9
--- /dev/null
+++ b/packages/grafana-data/src/vector/ArrayVector.ts
@@ -0,0 +1,37 @@
+import { MutableVector } from '../types/vector';
+
+export class ArrayVector implements MutableVector {
+ buffer: T[];
+
+ constructor(buffer?: T[]) {
+ this.buffer = buffer ? buffer : [];
+ }
+
+ get length() {
+ return this.buffer.length;
+ }
+
+ add(value: T) {
+ this.buffer.push(value);
+ }
+
+ get(index: number): T {
+ return this.buffer[index];
+ }
+
+ set(index: number, value: T) {
+ this.buffer[index] = value;
+ }
+
+ reverse() {
+ this.buffer.reverse();
+ }
+
+ toArray(): T[] {
+ return this.buffer;
+ }
+
+ toJSON(): T[] {
+ return this.buffer;
+ }
+}
diff --git a/packages/grafana-data/src/utils/vector.test.ts b/packages/grafana-data/src/vector/CircularVector.test.ts
similarity index 65%
rename from packages/grafana-data/src/utils/vector.test.ts
rename to packages/grafana-data/src/vector/CircularVector.test.ts
index 1806020ed59fa..abffd15846ba9 100644
--- a/packages/grafana-data/src/utils/vector.test.ts
+++ b/packages/grafana-data/src/vector/CircularVector.test.ts
@@ -1,31 +1,4 @@
-import { ConstantVector, ScaledVector, ArrayVector, CircularVector, AppendedVectors } from './vector';
-
-describe('Check Proxy Vector', () => {
- it('should support constant values', () => {
- const value = 3.5;
- const v = new ConstantVector(value, 7);
- expect(v.length).toEqual(7);
-
- expect(v.get(0)).toEqual(value);
- expect(v.get(1)).toEqual(value);
-
- // Now check all of them
- for (let i = 0; i < 10; i++) {
- expect(v.get(i)).toEqual(value);
- }
- });
-
- it('should support multiply operations', () => {
- const source = new ArrayVector([1, 2, 3, 4]);
- const scale = 2.456;
- const v = new ScaledVector(source, scale);
- expect(v.length).toEqual(source.length);
- // expect(v.push(10)).toEqual(source.length); // not implemented
- for (let i = 0; i < 10; i++) {
- expect(v.get(i)).toEqual(source.get(i) * scale);
- }
- });
-});
+import { CircularVector } from './CircularVector';
describe('Check Circular Vector', () => {
it('should append values', () => {
@@ -156,24 +129,3 @@ describe('Check Circular Vector', () => {
expect(v.toArray()).toEqual([3, 4, 5]);
});
});
-
-describe('Check Appending Vector', () => {
- it('should transparently join them', () => {
- const appended = new AppendedVectors();
- appended.append(new ArrayVector([1, 2, 3]));
- appended.append(new ArrayVector([4, 5, 6]));
- appended.append(new ArrayVector([7, 8, 9]));
- expect(appended.length).toEqual(9);
-
- appended.setLength(5);
- expect(appended.length).toEqual(5);
- appended.append(new ArrayVector(['a', 'b', 'c']));
- expect(appended.length).toEqual(8);
- expect(appended.toArray()).toEqual([1, 2, 3, 4, 5, 'a', 'b', 'c']);
-
- appended.setLength(2);
- appended.setLength(6);
- appended.append(new ArrayVector(['x', 'y', 'z']));
- expect(appended.toArray()).toEqual([1, 2, undefined, undefined, undefined, undefined, 'x', 'y', 'z']);
- });
-});
diff --git a/packages/grafana-data/src/vector/CircularVector.ts b/packages/grafana-data/src/vector/CircularVector.ts
new file mode 100644
index 0000000000000..f0eeb6579f036
--- /dev/null
+++ b/packages/grafana-data/src/vector/CircularVector.ts
@@ -0,0 +1,138 @@
+import { MutableVector } from '../types/vector';
+import { vectorToArray } from './vectorToArray';
+
+interface CircularOptions {
+ buffer?: T[];
+ append?: 'head' | 'tail';
+ capacity?: number;
+}
+
+/**
+ * Circular vector uses a single buffer to capture a stream of values
+ * overwriting the oldest value on add.
+ *
+ * This supports addting to the 'head' or 'tail' and will grow the buffer
+ * to match a configured capacity.
+ */
+export class CircularVector implements MutableVector {
+ private buffer: T[];
+ private index: number;
+ private capacity: number;
+ private tail: boolean;
+
+ constructor(options: CircularOptions) {
+ this.buffer = options.buffer || [];
+ this.capacity = this.buffer.length;
+ this.tail = 'head' !== options.append;
+ this.index = 0;
+
+ this.add = this.getAddFunction();
+ if (options.capacity) {
+ this.setCapacity(options.capacity);
+ }
+ }
+
+ /**
+ * This gets the appropriate add function depending on the buffer state:
+ * * head vs tail
+ * * growing buffer vs overwriting values
+ */
+ private getAddFunction() {
+ // When we are not at capacity, it should actually modify the buffer
+ if (this.capacity > this.buffer.length) {
+ if (this.tail) {
+ return (value: T) => {
+ this.buffer.push(value);
+ if (this.buffer.length >= this.capacity) {
+ this.add = this.getAddFunction();
+ }
+ };
+ } else {
+ return (value: T) => {
+ this.buffer.unshift(value);
+ if (this.buffer.length >= this.capacity) {
+ this.add = this.getAddFunction();
+ }
+ };
+ }
+ }
+
+ if (this.tail) {
+ return (value: T) => {
+ this.buffer[this.index] = value;
+ this.index = (this.index + 1) % this.buffer.length;
+ };
+ }
+
+ // Append values to the head
+ return (value: T) => {
+ let idx = this.index - 1;
+ if (idx < 0) {
+ idx = this.buffer.length - 1;
+ }
+ this.buffer[idx] = value;
+ this.index = idx;
+ };
+ }
+
+ setCapacity(v: number) {
+ if (this.capacity === v) {
+ return;
+ }
+ // Make a copy so it is in order and new additions can be at the head or tail
+ const copy = this.toArray();
+ if (v > this.length) {
+ this.buffer = copy;
+ } else if (v < this.capacity) {
+ // Shrink the buffer
+ const delta = this.length - v;
+ if (this.tail) {
+ this.buffer = copy.slice(delta, copy.length); // Keep last items
+ } else {
+ this.buffer = copy.slice(0, copy.length - delta); // Keep first items
+ }
+ }
+ this.capacity = v;
+ this.index = 0;
+ this.add = this.getAddFunction();
+ }
+
+ setAppendMode(mode: 'head' | 'tail') {
+ const tail = 'head' !== mode;
+ if (tail !== this.tail) {
+ this.buffer = this.toArray().reverse();
+ this.index = 0;
+ this.tail = tail;
+ this.add = this.getAddFunction();
+ }
+ }
+
+ reverse() {
+ this.buffer.reverse();
+ }
+
+ /**
+ * Add the value to the buffer
+ */
+ add: (value: T) => void;
+
+ get(index: number) {
+ return this.buffer[(index + this.index) % this.buffer.length];
+ }
+
+ set(index: number, value: T) {
+ this.buffer[(index + this.index) % this.buffer.length] = value;
+ }
+
+ get length() {
+ return this.buffer.length;
+ }
+
+ toArray(): T[] {
+ return vectorToArray(this);
+ }
+
+ toJSON(): T[] {
+ return vectorToArray(this);
+ }
+}
diff --git a/packages/grafana-data/src/vector/ConstantVector.test.ts b/packages/grafana-data/src/vector/ConstantVector.test.ts
new file mode 100644
index 0000000000000..b1b942209ee3b
--- /dev/null
+++ b/packages/grafana-data/src/vector/ConstantVector.test.ts
@@ -0,0 +1,17 @@
+import { ConstantVector } from './ConstantVector';
+
+describe('ConstantVector', () => {
+ it('should support constant values', () => {
+ const value = 3.5;
+ const v = new ConstantVector(value, 7);
+ expect(v.length).toEqual(7);
+
+ expect(v.get(0)).toEqual(value);
+ expect(v.get(1)).toEqual(value);
+
+ // Now check all of them
+ for (let i = 0; i < 10; i++) {
+ expect(v.get(i)).toEqual(value);
+ }
+ });
+});
diff --git a/packages/grafana-data/src/vector/ConstantVector.ts b/packages/grafana-data/src/vector/ConstantVector.ts
new file mode 100644
index 0000000000000..07b3beddc19cb
--- /dev/null
+++ b/packages/grafana-data/src/vector/ConstantVector.ts
@@ -0,0 +1,22 @@
+import { Vector } from '../types/vector';
+
+export class ConstantVector implements Vector {
+ constructor(private value: T, private len: number) {}
+
+ get length() {
+ return this.len;
+ }
+
+ get(index: number): T {
+ return this.value;
+ }
+
+ toArray(): T[] {
+ const arr = new Array(this.length);
+ return arr.fill(this.value);
+ }
+
+ toJSON(): T[] {
+ return this.toArray();
+ }
+}
diff --git a/packages/grafana-data/src/vector/ScaledVector.test.ts b/packages/grafana-data/src/vector/ScaledVector.test.ts
new file mode 100644
index 0000000000000..b395113559485
--- /dev/null
+++ b/packages/grafana-data/src/vector/ScaledVector.test.ts
@@ -0,0 +1,15 @@
+import { ArrayVector } from './ArrayVector';
+import { ScaledVector } from './ScaledVector';
+
+describe('ScaledVector', () => {
+ it('should support multiply operations', () => {
+ const source = new ArrayVector([1, 2, 3, 4]);
+ const scale = 2.456;
+ const v = new ScaledVector(source, scale);
+ expect(v.length).toEqual(source.length);
+ // expect(v.push(10)).toEqual(source.length); // not implemented
+ for (let i = 0; i < 10; i++) {
+ expect(v.get(i)).toEqual(source.get(i) * scale);
+ }
+ });
+});
diff --git a/packages/grafana-data/src/vector/ScaledVector.ts b/packages/grafana-data/src/vector/ScaledVector.ts
new file mode 100644
index 0000000000000..0656291d7d07c
--- /dev/null
+++ b/packages/grafana-data/src/vector/ScaledVector.ts
@@ -0,0 +1,22 @@
+import { Vector } from '../types/vector';
+import { vectorToArray } from './vectorToArray';
+
+export class ScaledVector implements Vector {
+ constructor(private source: Vector, private scale: number) {}
+
+ get length(): number {
+ return this.source.length;
+ }
+
+ get(index: number): number {
+ return this.source.get(index) * this.scale;
+ }
+
+ toArray(): number[] {
+ return vectorToArray(this);
+ }
+
+ toJSON(): number[] {
+ return vectorToArray(this);
+ }
+}
diff --git a/packages/grafana-data/src/vector/SortedVector.ts b/packages/grafana-data/src/vector/SortedVector.ts
new file mode 100644
index 0000000000000..33b97f6778fa1
--- /dev/null
+++ b/packages/grafana-data/src/vector/SortedVector.ts
@@ -0,0 +1,25 @@
+import { Vector } from '../types/vector';
+import { vectorToArray } from './vectorToArray';
+
+/**
+ * Values are returned in the order defined by the input parameter
+ */
+export class SortedVector implements Vector {
+ constructor(private source: Vector, private order: number[]) {}
+
+ get length(): number {
+ return this.source.length;
+ }
+
+ get(index: number): T {
+ return this.source.get(this.order[index]);
+ }
+
+ toArray(): T[] {
+ return vectorToArray(this);
+ }
+
+ toJSON(): T[] {
+ return vectorToArray(this);
+ }
+}
diff --git a/packages/grafana-data/src/vector/index.ts b/packages/grafana-data/src/vector/index.ts
new file mode 100644
index 0000000000000..392cb37961088
--- /dev/null
+++ b/packages/grafana-data/src/vector/index.ts
@@ -0,0 +1,6 @@
+export * from './AppendedVectors';
+export * from './ArrayVector';
+export * from './CircularVector';
+export * from './ConstantVector';
+export * from './ScaledVector';
+export * from './SortedVector';
diff --git a/packages/grafana-data/src/vector/vectorToArray.ts b/packages/grafana-data/src/vector/vectorToArray.ts
new file mode 100644
index 0000000000000..34212651ab835
--- /dev/null
+++ b/packages/grafana-data/src/vector/vectorToArray.ts
@@ -0,0 +1,9 @@
+import { Vector } from '../types/vector';
+
+export function vectorToArray(v: Vector): T[] {
+ const arr: T[] = [];
+ for (let i = 0; i < v.length; i++) {
+ arr[i] = v.get(i);
+ }
+ return arr;
+}
diff --git a/packages/grafana-ui/src/components/Button/AbstractButton.tsx b/packages/grafana-ui/src/components/Button/AbstractButton.tsx
index 030d03fcdd2aa..6ce86ac4176d4 100644
--- a/packages/grafana-ui/src/components/Button/AbstractButton.tsx
+++ b/packages/grafana-ui/src/components/Button/AbstractButton.tsx
@@ -19,7 +19,9 @@ export interface CommonButtonProps {
className?: string;
}
-export interface LinkButtonProps extends CommonButtonProps, AnchorHTMLAttributes {}
+export interface LinkButtonProps extends CommonButtonProps, AnchorHTMLAttributes {
+ disabled?: boolean;
+}
export interface ButtonProps extends CommonButtonProps, ButtonHTMLAttributes {}
interface AbstractButtonProps extends CommonButtonProps, Themeable {
diff --git a/packages/grafana-ui/src/components/RefreshPicker/RefreshPicker.tsx b/packages/grafana-ui/src/components/RefreshPicker/RefreshPicker.tsx
index 92a55d71ac072..72f1ae342146e 100644
--- a/packages/grafana-ui/src/components/RefreshPicker/RefreshPicker.tsx
+++ b/packages/grafana-ui/src/components/RefreshPicker/RefreshPicker.tsx
@@ -1,24 +1,48 @@
import React, { PureComponent } from 'react';
import classNames from 'classnames';
import { SelectableValue } from '@grafana/data';
+import { css } from 'emotion';
import { Tooltip } from '../Tooltip/Tooltip';
import { ButtonSelect } from '../Select/ButtonSelect';
+import memoizeOne from 'memoize-one';
+import { GrafanaTheme } from '../../types';
+import { withTheme } from '../../themes';
export const offOption = { label: 'Off', value: '' };
export const liveOption = { label: 'Live', value: 'LIVE' };
export const defaultIntervals = ['5s', '10s', '30s', '1m', '5m', '15m', '30m', '1h', '2h', '1d'];
export const isLive = (refreshInterval: string): boolean => refreshInterval === liveOption.value;
+const getStyles = memoizeOne((theme: GrafanaTheme) => {
+ return {
+ selectButton: css`
+ label: selectButton;
+ .select-button-value {
+ color: ${theme.colors.orange};
+ }
+ `,
+ };
+});
+
export interface Props {
intervals?: string[];
- onRefresh: () => any;
+ onRefresh?: () => any;
onIntervalChanged: (interval: string) => void;
value?: string;
- tooltip: string;
+ tooltip?: string;
hasLiveOption?: boolean;
+ // You can supply your own refresh button element. In that case onRefresh and tooltip are ignored.
+ refreshButton?: React.ReactNode;
+ buttonSelectClassName?: string;
+ theme: GrafanaTheme;
}
-export class RefreshPicker extends PureComponent {
+export class RefreshPickerBase extends PureComponent {
+ // Make it exported as static properties to be easier to access. The global exports need to be accessed by direct
+ // import of this source file which won't work if this was installed as package.
+ static offOption = offOption;
+ static liveOption = liveOption;
+
constructor(props: Props) {
super(props);
}
@@ -46,10 +70,11 @@ export class RefreshPicker extends PureComponent {
};
render() {
- const { onRefresh, intervals, tooltip, value } = this.props;
+ const { onRefresh, intervals, tooltip, value, refreshButton, buttonSelectClassName, theme } = this.props;
const options = this.intervalsToOptions(intervals);
const currentValue = value || '';
const selectedValue = options.find(item => item.value === currentValue) || offOption;
+ const styles = getStyles(theme);
const cssClasses = classNames({
'refresh-picker': true,
@@ -60,13 +85,20 @@ export class RefreshPicker extends PureComponent {
return (
+
\ No newline at end of file
diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/query.editor.html b/public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/query.editor.html
index a5bed66c9df81..5b2d1010ca97c 100644
--- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/query.editor.html
+++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/query.editor.html
@@ -7,70 +7,138 @@
ng-change="ctrl.onQueryTypeChange()">
-