Merge remote-tracking branch 'origin/master' into zoltan/test-ci

This commit is contained in:
Zoltán Bedi
2020-11-09 14:00:27 +01:00
517 changed files with 8702 additions and 4828 deletions
+36 -44
View File
@@ -17,7 +17,7 @@ steps:
image: grafana/build-container:1.2.28
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/grabpl
- chmod +x bin/grabpl
- curl -fLO https://github.com/jwilder/dockerize/releases/download/v$${DOCKERIZE_VERSION}/dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
- tar -C bin -xzvf dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
@@ -142,6 +142,9 @@ steps:
image: grafana/build-container:1.2.28
commands:
- yarn storybook:build
- ./bin/grabpl verify-storybook
environment:
NODE_OPTIONS: --max_old_space_size=4096
depends_on:
- package
@@ -231,32 +234,6 @@ trigger:
event:
- pull_request
---
kind: pipeline
type: docker
name: windows-pr
platform:
os: windows
arch: amd64
version: 1809
steps:
- name: identify-runner
image: mcr.microsoft.com/windows:1809
commands:
- echo $env:DRONE_RUNNER_NAME
- name: initialize
image: grafana/ci-wix:0.1.1
commands:
- $$ProgressPreference = "SilentlyContinue"
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/windows/grabpl.exe -OutFile grabpl.exe
trigger:
event:
- pull_request
---
kind: pipeline
type: docker
@@ -276,7 +253,7 @@ steps:
image: grafana/build-container:1.2.28
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/grabpl
- chmod +x bin/grabpl
- curl -fLO https://github.com/jwilder/dockerize/releases/download/v$${DOCKERIZE_VERSION}/dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
- tar -C bin -xzvf dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
@@ -438,6 +415,9 @@ steps:
image: grafana/build-container:1.2.28
commands:
- yarn storybook:build
- ./bin/grabpl verify-storybook
environment:
NODE_OPTIONS: --max_old_space_size=4096
depends_on:
- package
@@ -608,7 +588,7 @@ steps:
image: grafana/ci-wix:0.1.1
commands:
- $$ProgressPreference = "SilentlyContinue"
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/windows/grabpl.exe -OutFile grabpl.exe
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/windows/grabpl.exe -OutFile grabpl.exe
- name: build-windows-installer
image: grafana/ci-wix:0.1.1
@@ -657,7 +637,7 @@ steps:
image: grafana/build-container:1.2.28
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/grabpl
- chmod +x bin/grabpl
environment:
DOCKERIZE_VERSION: 0.6.1
@@ -732,7 +712,7 @@ steps:
image: grafana/build-container:1.2.28
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/grabpl
- chmod +x bin/grabpl
- ./bin/grabpl verify-version ${DRONE_TAG}
- curl -fLO https://github.com/jwilder/dockerize/releases/download/v$${DOCKERIZE_VERSION}/dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
@@ -875,6 +855,9 @@ steps:
image: grafana/build-container:1.2.28
commands:
- yarn storybook:build
- ./bin/grabpl verify-storybook
environment:
NODE_OPTIONS: --max_old_space_size=4096
depends_on:
- package
@@ -1027,7 +1010,7 @@ steps:
image: grafana/ci-wix:0.1.1
commands:
- $$ProgressPreference = "SilentlyContinue"
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/windows/grabpl.exe -OutFile grabpl.exe
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/windows/grabpl.exe -OutFile grabpl.exe
- name: build-windows-installer
image: grafana/ci-wix:0.1.1
@@ -1077,7 +1060,7 @@ steps:
image: grafana/build-container:1.2.28
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/grabpl
- chmod +x bin/grabpl
- git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git"
- cd grafana-enterprise
@@ -1361,7 +1344,7 @@ steps:
image: grafana/ci-wix:0.1.1
commands:
- $$ProgressPreference = "SilentlyContinue"
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/windows/grabpl.exe -OutFile grabpl.exe
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/windows/grabpl.exe -OutFile grabpl.exe
- git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git"
- cd grafana-enterprise
- git checkout ${DRONE_TAG}
@@ -1426,7 +1409,7 @@ steps:
image: grafana/build-container:1.2.28
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/grabpl
- chmod +x bin/grabpl
- ./bin/grabpl verify-version ${DRONE_TAG}
environment:
@@ -1503,7 +1486,7 @@ steps:
image: grafana/build-container:1.2.28
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/grabpl
- chmod +x bin/grabpl
- ./bin/grabpl verify-version v7.3.0-test
- curl -fLO https://github.com/jwilder/dockerize/releases/download/v$${DOCKERIZE_VERSION}/dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
@@ -1646,6 +1629,9 @@ steps:
image: grafana/build-container:1.2.28
commands:
- yarn storybook:build
- ./bin/grabpl verify-storybook
environment:
NODE_OPTIONS: --max_old_space_size=4096
depends_on:
- package
@@ -1788,7 +1774,7 @@ steps:
image: grafana/ci-wix:0.1.1
commands:
- $$ProgressPreference = "SilentlyContinue"
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/windows/grabpl.exe -OutFile grabpl.exe
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/windows/grabpl.exe -OutFile grabpl.exe
- name: build-windows-installer
image: grafana/ci-wix:0.1.1
@@ -1838,7 +1824,7 @@ steps:
image: grafana/build-container:1.2.28
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/grabpl
- chmod +x bin/grabpl
- git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git"
- cd grafana-enterprise
@@ -2116,7 +2102,7 @@ steps:
image: grafana/ci-wix:0.1.1
commands:
- $$ProgressPreference = "SilentlyContinue"
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/windows/grabpl.exe -OutFile grabpl.exe
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/windows/grabpl.exe -OutFile grabpl.exe
- git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git"
- cd grafana-enterprise
- git checkout master
@@ -2181,7 +2167,7 @@ steps:
image: grafana/build-container:1.2.28
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/grabpl
- chmod +x bin/grabpl
- ./bin/grabpl verify-version v7.3.0-test
environment:
@@ -2267,7 +2253,7 @@ steps:
image: grafana/build-container:1.2.28
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/grabpl
- chmod +x bin/grabpl
- curl -fLO https://github.com/jwilder/dockerize/releases/download/v$${DOCKERIZE_VERSION}/dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
- tar -C bin -xzvf dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
@@ -2406,6 +2392,9 @@ steps:
image: grafana/build-container:1.2.28
commands:
- yarn storybook:build
- ./bin/grabpl verify-storybook
environment:
NODE_OPTIONS: --max_old_space_size=4096
depends_on:
- package
@@ -2506,7 +2495,7 @@ steps:
image: grafana/ci-wix:0.1.1
commands:
- $$ProgressPreference = "SilentlyContinue"
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/windows/grabpl.exe -OutFile grabpl.exe
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/windows/grabpl.exe -OutFile grabpl.exe
- name: build-windows-installer
image: grafana/ci-wix:0.1.1
@@ -2552,7 +2541,7 @@ steps:
image: grafana/build-container:1.2.28
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/grabpl
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/grabpl
- chmod +x bin/grabpl
- git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git"
- cd grafana-enterprise
@@ -2709,6 +2698,9 @@ steps:
image: grafana/build-container:1.2.28
commands:
- yarn storybook:build
- ./bin/grabpl verify-storybook
environment:
NODE_OPTIONS: --max_old_space_size=4096
depends_on:
- package
@@ -2812,7 +2804,7 @@ steps:
image: grafana/ci-wix:0.1.1
commands:
- $$ProgressPreference = "SilentlyContinue"
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/windows/grabpl.exe -OutFile grabpl.exe
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.24/windows/grabpl.exe -OutFile grabpl.exe
- git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git"
- cd grafana-enterprise
- git checkout $$env:DRONE_BRANCH
+1 -1
View File
@@ -1 +1 @@
* text=auto
* text=auto eol=lf
+2 -2
View File
@@ -119,5 +119,5 @@ compilation-stats.json
# report dumping the whole system env
/report.*.json
# auto generated front end docs
/docs/sources/packages_api/*
# auto generated frontend docs
/docs/sources/packages_api
+35
View File
@@ -1,3 +1,38 @@
# 7.4.0 (unreleased)
### Breaking changes
We have upgraded AngularJS from version 1.6.6 to 1.8.2. Due to this upgrade some old angular plugins might stop working and will require an small update. This is due to the deprecation and removal of pre-assigned bindings. So if your custom angular controllers expect component bindings in the controller constructor you need to move this code to an `$onInit` function. For more details on how to migrate AngularJS code open the [migration guide](https://docs.angularjs.org/guide/migration) and search for **pre-assigning bindings**.
In order not to break all angular panel plugins and data sources we have some custom [angular inject behavior](https://github.com/grafana/grafana/blob/master/public/app/core/injectorMonkeyPatch.ts) that makes sure that bindings for these controllers are still set before constructor is called so many old angular panels and data source plugins will still work.
# 7.3.1 (2020-10-30)
### Bug Fixes
* **Cloudwatch**: Fix duplicate metric data. [#28642](https://github.com/grafana/grafana/pull/28642), [@zoltanbedi](https://github.com/zoltanbedi)
* **Loki**: Fix error when some queries return zero results. [#28645](https://github.com/grafana/grafana/pull/28645), [@ivanahuckova](https://github.com/ivanahuckova)
* **PanelMenu**: Fix panel submenu not being accessible for panels close to the right edge of the screen. [#28666](https://github.com/grafana/grafana/pull/28666), [@torkelo](https://github.com/torkelo)
* **Plugins**: Fix descendent frontend plugin signature validation. [#28638](https://github.com/grafana/grafana/pull/28638), [@wbrowne](https://github.com/wbrowne)
* **StatPanel**: Fix value being under graph and reduced likelihood for white and dark value text mixing. [#28641](https://github.com/grafana/grafana/pull/28641), [@torkelo](https://github.com/torkelo)
* **TextPanel**: Fix problems where text panel would show old content. [#28643](https://github.com/grafana/grafana/pull/28643), [@torkelo](https://github.com/torkelo)
# 7.3.0 (2020-10-28)
### Features / Enhancements
* **AzureMonitor**: Support decimal (as float64) type in analytics/logs. [#28480](https://github.com/grafana/grafana/pull/28480), [@kylebrandt](https://github.com/kylebrandt)
* **Plugins signing**: UI information. [#28469](https://github.com/grafana/grafana/pull/28469), [@dprokop](https://github.com/dprokop)
* **Short URL**: Update last seen at when visiting a short URL. [#28565](https://github.com/grafana/grafana/pull/28565), [@marefr](https://github.com/marefr)
### Bug Fixes
* **Alerting**: Log warnings for obsolete notifiers when extracting alerts and remove frequent error log messages. [#28162](https://github.com/grafana/grafana/pull/28162), [@papagian](https://github.com/papagian)
* **Auth**: Fix SigV4 request verification step for Amazon Elasticsearch Service. [#28481](https://github.com/grafana/grafana/pull/28481), [@wbrowne](https://github.com/wbrowne)
* **Auth**: Should redirect to login when anonymous enabled and URL with different org than anonymous specified. [#28158](https://github.com/grafana/grafana/pull/28158), [@marefr](https://github.com/marefr)
* **Elasticsearch**: Fix handling of errors when testing data source. [#28498](https://github.com/grafana/grafana/pull/28498), [@marefr](https://github.com/marefr)
* **Graphite**: Fix default version to be 1.1. [#28471](https://github.com/grafana/grafana/pull/28471), [@ivanahuckova](https://github.com/ivanahuckova)
* **StatPanel**: Fixes BizChart error max: yyy should not be less than min zzz. [#28587](https://github.com/grafana/grafana/pull/28587), [@hugohaggmark](https://github.com/hugohaggmark)
# 7.3.0-beta2 (2020-10-22)
### Features / Enhancements
+3 -3
View File
@@ -8,7 +8,7 @@ The open-source platform for monitoring and observability.
Grafana allows you to query, visualize, alert on and understand your metrics no matter where they are stored. Create, explore, and share dashboards with your team and foster a data driven culture:
- **Visualize:** Fast and flexible client side graphs with a multitude of options. Panel plugins for many different way to visualize metrics and logs.
- **Visualize:** Fast and flexible client side graphs with a multitude of options. Panel plugins offer many different ways to visualize metrics and logs.
- **Dynamic Dashboards:** Create dynamic & reusable dashboards with template variables that appear as dropdowns at the top of the dashboard.
- **Explore Metrics:** Explore your data through ad-hoc queries and dynamic drilldown. Split view and compare different time ranges, queries and data sources side by side.
- **Explore Logs:** Experience the magic of switching from metrics to logs with preserved label filters. Quickly search through all your logs or streaming them live.
@@ -36,8 +36,8 @@ If you're interested in contributing to the Grafana project:
## Get involved
- Follow [@grafana on Twitter](https://twitter.com/grafana/)
- Read and subscribe to the [Grafana blog](https://grafana.com/blog/)
- Follow [@grafana on Twitter](https://twitter.com/grafana/).
- Read and subscribe to the [Grafana blog](https://grafana.com/blog/).
- If you have a specific question, check out our [discussion forums](https://community.grafana.com/).
- For general discussions, join us on the [official Slack](http://slack.raintank.io/) team.
+26 -5
View File
@@ -37,6 +37,7 @@ var (
libc string
pkgArch string
version string = "v1"
buildTags []string
// deb & rpm does not support semver so have to handle their version a little differently
linuxPackageVersion string = "v1"
linuxPackageIteration string = ""
@@ -59,11 +60,13 @@ func main() {
log.SetFlags(0)
var buildIdRaw string
var buildTagsRaw string
flag.StringVar(&goarch, "goarch", runtime.GOARCH, "GOARCH")
flag.StringVar(&goos, "goos", runtime.GOOS, "GOOS")
flag.StringVar(&gocc, "cc", "", "CC")
flag.StringVar(&libc, "libc", "", "LIBC")
flag.StringVar(&buildTagsRaw, "build-tags", "", "Sets custom build tags")
flag.BoolVar(&cgo, "cgo-enabled", cgo, "Enable cgo")
flag.StringVar(&pkgArch, "pkg-arch", "", "PKG ARCH")
flag.BoolVar(&race, "race", race, "Use race detector")
@@ -89,6 +92,10 @@ func main() {
return
}
if len(buildTagsRaw) > 0 {
buildTags = strings.Split(buildTagsRaw, ",")
}
log.Printf("Version: %s, Linux Version: %s, Package Iteration: %s\n", version, linuxPackageVersion, linuxPackageIteration)
if flag.NArg() == 0 {
@@ -105,16 +112,16 @@ func main() {
case "build-srv", "build-server":
clean()
doBuild("grafana-server", "./pkg/cmd/grafana-server", []string{})
doBuild("grafana-server", "./pkg/cmd/grafana-server", buildTags)
case "build-cli":
clean()
doBuild("grafana-cli", "./pkg/cmd/grafana-cli", []string{})
doBuild("grafana-cli", "./pkg/cmd/grafana-cli", buildTags)
case "build":
//clean()
for _, binary := range binaries {
doBuild(binary, "./pkg/cmd/"+binary, []string{})
doBuild(binary, "./pkg/cmd/"+binary, buildTags)
}
case "build-frontend":
@@ -451,7 +458,12 @@ func gruntBuildArg(task string) []string {
}
func setup() {
runPrint("go", "install", "-v", "./pkg/cmd/grafana-server")
args := []string{"install", "-v"}
if goos == windows {
args = append(args, "-buildmode=exe")
}
args = append(args, "./pkg/cmd/grafana-server")
runPrint("go", args...)
}
func printGeneratedVersion() {
@@ -460,7 +472,12 @@ func printGeneratedVersion() {
func test(pkg string) {
setBuildEnv()
runPrint("go", "test", "-short", "-timeout", "60s", pkg)
args := []string{"test", "-short", "-timeout", "60s"}
if goos == windows {
args = append(args, "-buildmode=exe")
}
args = append(args, pkg)
runPrint("go", args...)
}
func doBuild(binaryName, pkg string, tags []string) {
@@ -482,6 +499,10 @@ func doBuild(binaryName, pkg string, tags []string) {
rmr(binary, binary+".md5")
}
args := []string{"build", "-ldflags", ldflags()}
if goos == windows {
// Work around a linking error on Windows: "export ordinal too large"
args = append(args, "-buildmode=exe")
}
if len(tags) > 0 {
args = append(args, "-tags", strings.Join(tags, ","))
}
+2 -1
View File
@@ -671,7 +671,7 @@ disable_total_stats = false
basic_auth_username =
basic_auth_password =
# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which
# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which
# can expose more information about the Grafana instance.
[metrics.environment_info]
#exampleLabel1 = exampleValue1
@@ -769,6 +769,7 @@ enable_alpha = false
app_tls_skip_verify_insecure = false
# Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature.
allow_loading_unsigned_plugins =
marketplace_url = https://grafana.com/grafana/plugins/
#################################### Grafana Image Renderer Plugin ##########################
[plugin.grafana-image-renderer]
+2 -1
View File
@@ -665,7 +665,7 @@
; basic_auth_username =
; basic_auth_password =
# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which
# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which
# can expose more information about the Grafana instance.
[metrics.environment_info]
#exampleLabel1 = exampleValue1
@@ -757,6 +757,7 @@
;app_tls_skip_verify_insecure = false
# Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature.
;allow_loading_unsigned_plugins =
;marketplace_url = https://grafana.com/grafana/plugins/
#################################### Grafana Image Renderer Plugin ##########################
[plugin.grafana-image-renderer]
+11 -1
View File
@@ -76,7 +76,10 @@ When you log in for the first time, Grafana asks you to change your password.
#### Building on Windows
The Grafana backend includes Sqlite3 which requires GCC to compile. So in order to compile Grafana on Windows you need to install GCC. We recommend [TDM-GCC](http://tdm-gcc.tdragon.net/download).
The Grafana backend includes SQLite which requires GCC to compile. So in order to compile Grafana on Windows you need to install GCC. We recommend [TDM-GCC](http://tdm-gcc.tdragon.net/download). Eventually, if you use [Scoop](https://scoop.sh), you can install GCC through that.
You can simply build the back-end as follows: `go run build.go build`. The Grafana binaries will be in bin\\windows-amd64.
Alternately, if you wish to use the `make` command, install [Make for Windows](http://gnuwin32.sourceforge.net/packages/make.htm) and use it in a Unix shell (f.ex. Git Bash).
## Test Grafana
@@ -98,6 +101,13 @@ If you're developing for the backend, run the tests with the standard Go tool:
go test -v ./pkg/...
```
#### On Windows
Running the backend tests on Windows currently needs some tweaking, so use the build.go script:
```
go run build.go test
```
### Run end-to-end tests
The end to end tests in Grafana use [Cypress](https://www.cypress.io/) to run automated scripts in a headless Chromium browser. Read more about our [e2e framework](/contribute/style-guides/e2e.md).
@@ -51,12 +51,12 @@ The following sections provide general guidelines on topics specific to Grafana
### General
- Use active voice. Avoid passive voice.
- Passive: The heatmap visualization is displayed.
- Active: Grafana displays the heatmap visualization.
- Passive: The heatmap visualization is displayed.
- Write in the imperative second person. Examples: You can write a query. Click the panel. Close the window.
- Write in present tense.
- Not: The panel will open.
- Use: The panel opens. Grafana opens the panel.
- Not: The panel will open.
- Do not use an ampersand (&) as an abbreviation for "and."
- **Exceptions:** If an ampersand is used in the Grafana UI, then match the UI.
- Avoid using internal slang and jargon in technical documentation.
@@ -78,9 +78,9 @@ The following sections provide general guidelines on topics specific to Grafana
- Task topic headings start with a verb.
- Write a query. Create a dashboard.
- Concept and reference topic headings should be nouns or gerunds. Examples: Contributing to docs, Visualizations, Style guide
- Avoid following one heading with another heading.
- Avoid stacked headings, which is following one heading with another heading.
- Avoid skipping heading levels. For example, an h1 should be followed by an h2 rather than an h3.
- Avoid having just one lower-level heading. For example, h1, h2, h2, h3, h3, h2 is a good order. Do not go h1, h2, h3, h2, h3, h2.
- Avoid having just one lower-level heading. For example, h1, h2, h2, h3, h3, h2, h2 is a good order. Do not go h1, h2, h3, h2, h3, h2.
- Don't include parenthetical words like (Important!) in headings.
### Images
@@ -152,9 +152,8 @@ Warnings tell the user not to do something. For example:
- Do not assume everyone is using Linux. Make sure instructions include enough information for Windows and Mac users to successfully complete procedures.
- Do not add `$` before commands. Make it easy for users to copy and paste commands.
- **Wrong:** `$ sudo yum install grafana`
- **Right:** `sudo yum install grafana`
- **Wrong:** `$ sudo yum install grafana`
- Include `sudo` before commands that require `sudo` to work.
@@ -185,7 +184,7 @@ Two words if used as a verb, one word if used as a noun.
#### data source
Two words, not one
Two words, not one.
**Exceptions:**
- "datasource" used as an identifier
@@ -213,6 +212,10 @@ Do not use. This is developer jargon that refers to a UI panel. Refer to the pan
One word, not two.
#### mixin
One word, not two. Also, not hyphenated.
#### open source, open-source
Do not hyphenate when used as an adjective unless the lack of hyphen would cause confusion. For example: _Open source software design is the most open open-source system I can imagine._
@@ -223,14 +226,14 @@ Do not hyphenate when it is used as a noun. For example: _Open source is the bes
Two words if used as a verb, one word if used as a noun.
**Examples**
- Set up the workspace.
- Initial setup might take five minutes.
### node_exporter, windows_exporter
When referencing the Prometheus data source exporters, always use "node_exporter" and "windows_exporter" when referring to those tools.
**Correct:** node_exporter, windows_exporter
**Incorrect:** Node Exporter, node exporter, Windows Exporter, Windows exporter, windows exporter
**Examples**
- Set up the workspace.
- Initial setup might take five minutes.
**Incorrect:** Node Exporter, node exporter, Windows Exporter, Windows exporter, windows exporter.
@@ -34,7 +34,7 @@
},
"id": 11,
"options": {
"content": "## Global variables\n\n* `__dashboard` = `${__dashboard}`\n* `__dashboard.name` = `${__dashboard.name}`\n* `__dashboard.uid` = `${__dashboard.uid}`\n* `__org.name` = `${__org.name}`\n* `__org.id` = `${__org.id}`\n* `__user.id` = `${__user.id}`\n* `__user.login` = `${__user.login}`\n \n## Formats\n\n* `Server:raw` = `${Server:raw}`\n* `Server:regex` = `${Server:regex}`\n* `Server:lucene` = `${Server:lucene}`\n* `Server:glob` = `${Server:glob}`\n* `Server:pipe` = `${Server:pipe}`\n* `Server:distributed` = `${Server:distributed}`\n* `Server:csv` = `${Server:csv}`\n* `Server:html` = `${Server:html}`\n* `Server:json` = `${Server:json}`\n* `Server:percentencode` = `${Server:percentencode}`\n* `Server:singlequote` = `${Server:singlequote}`\n* `Server:doublequote` = `${Server:doublequote}`\n* `Server:sqlstring` = `${Server:sqlstring}`\n* `Server:date` = `${Server:date}`\n* `Server:text` = `${Server:text}`\n\n",
"content": "## Global variables\n\n* `__dashboard` = `${__dashboard}`\n* `__dashboard.name` = `${__dashboard.name}`\n* `__dashboard.uid` = `${__dashboard.uid}`\n* `__org.name` = `${__org.name}`\n* `__org.id` = `${__org.id}`\n* `__user.id` = `${__user.id}`\n* `__user.login` = `${__user.login}`\n* `__user.email` = `${__user.email}`\n \n## Formats\n\n* `Server:raw` = `${Server:raw}`\n* `Server:regex` = `${Server:regex}`\n* `Server:lucene` = `${Server:lucene}`\n* `Server:glob` = `${Server:glob}`\n* `Server:pipe` = `${Server:pipe}`\n* `Server:distributed` = `${Server:distributed}`\n* `Server:csv` = `${Server:csv}`\n* `Server:html` = `${Server:html}`\n* `Server:json` = `${Server:json}`\n* `Server:percentencode` = `${Server:percentencode}`\n* `Server:singlequote` = `${Server:singlequote}`\n* `Server:doublequote` = `${Server:doublequote}`\n* `Server:sqlstring` = `${Server:sqlstring}`\n* `Server:date` = `${Server:date}`\n* `Server:text` = `${Server:text}`\n\n",
"mode": "markdown"
},
"pluginVersion": "7.1.0",
+11 -4
View File
@@ -1,5 +1,12 @@
FROM mcr.microsoft.com/mssql/server:2017-CU4-ubuntu
FROM mcr.microsoft.com/mssql/server:2019-CU8-ubuntu-18.04
WORKDIR /usr/setup
COPY . /usr/setup
RUN chmod +x /usr/setup/setup.sh
CMD /bin/bash ./entrypoint.sh
COPY setup.sh setup.sql.template entrypoint.sh ./
USER root
RUN chmod +x setup.sh
RUN chown -R mssql ./
USER mssql
CMD /bin/bash ./entrypoint.sh
+2 -1
View File
@@ -1,4 +1,5 @@
#/bin/bash
set -eo pipefail
#wait for the SQL Server to come up
sleep 15s
@@ -9,4 +10,4 @@ cat /usr/setup/setup.sql.template | awk '{
gsub(/%%PWD%%/,"'$MSSQL_PASSWORD'")
}1' > /usr/setup/setup.sql
/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P $MSSQL_SA_PASSWORD -d master -i /usr/setup/setup.sql
/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P $MSSQL_SA_PASSWORD -d master -i /usr/setup/setup.sql
+1 -1
View File
@@ -1,4 +1,4 @@
FROM nginx:alpine
FROM nginx:1.19.3-alpine
COPY nginx.conf /etc/nginx/nginx.conf
COPY htpasswd /etc/nginx/htpasswd
@@ -1,4 +1,4 @@
FROM nginx:alpine
FROM nginx:1.19.3-alpine
COPY nginx.conf /etc/nginx/nginx.conf
COPY htpasswd /etc/nginx/htpasswd
@@ -1,4 +1,4 @@
FROM nginx:alpine
FROM nginx:1.19.3-alpine
COPY nginx.conf /etc/nginx/nginx.conf
COPY htpasswd /etc/nginx/htpasswd
@@ -2,5 +2,6 @@
# http://prometheus:9090 (Prometheus inside the docker compose)
nginxproxy:
build: docker/blocks/nginx_proxy
network_mode: host
build: docker/blocks/prometheus_basic_auth_proxy
ports:
- "10090:10090"
@@ -2,6 +2,7 @@ events { }
http {
server {
error_log stderr info;
listen 10090;
+8 -17
View File
@@ -1,23 +1,14 @@
.PHONY: docs docs-test docs-no-pull docs-generate-frontend docs-no-generate-frontend
.PHONY: docs docs-no-pull docs-test
IMAGE = grafana/docs-base:latest
IMAGE = grafana/grafana-docs-dev:latest
docs-generate-frontend:
yarn install --pure-lockfile
yarn run packages:build
yarn run packages:docsExtract
yarn run packages:docsToMarkdown
yarn run packages:clean
docs-no-generate-frontend:
docker run -v $(shell pwd)/sources:/hugo/content/docs/grafana/latest -p 3002:3002 --rm -it $(IMAGE) /bin/bash -c 'make server'
docs: docs-generate-frontend
docs:
docker pull $(IMAGE)
docker run -v $(shell pwd)/sources:/hugo/content/docs/grafana/latest -p 3002:3002 --rm -it $(IMAGE) /bin/bash -c 'make server'
docker run -v $(shell pwd)/sources:/hugo/content/docs/grafana/latest -p 3002:3002 --rm -it $(IMAGE)
docs-no-pull:
docker run -v $(shell pwd)/sources:/hugo/content/docs/grafana/latest -p 3002:3002 --rm -it $(IMAGE)
docs-no-pull: docs-generate-frontend docs-no-generate-frontend
docs-test: docs-generate-frontend
docs-test:
docker pull $(IMAGE)
docker run -v $(shell pwd)/sources:/hugo/content/docs/grafana/latest --rm -it $(IMAGE) /bin/bash -c 'make prod'
+4 -4
View File
@@ -9,8 +9,8 @@ Yarn >= 1.22.4
## Build the doc site
1. In the command line, make sure you are in the docs folder: `cd docs`.
1. Run `make docs`. This launches a preview of the docs website at `http://localhost:3002/docs/grafana/latest/` which will refresh automatically when changes to content in the `sources` directory are made.
1. On the command line, first change to the docs folder: `cd docs`.
1. Run `make docs`. This launches a preview of the docs website at `http://localhost:3002/docs/grafana/latest/` which will refresh automatically when changes are made to content in the `sources` directory.
---
@@ -34,6 +34,6 @@ Images are currently hosted in the grafana/website repo.
## Deploy changes to grafana.com
When a PR is merged to master with changes in the `docs/sources` directory, those changes are automatically synched to the grafana/website repo and published to the staging site.
When a PR is merged to master with changes in the `docs/sources` directory, those changes are automatically synced to the grafana/website repo and published to the staging site.
Generally, someone from marketing will publish to production each day, so as long as the sync is successful your docs edits will be published. Alternatively, you can refer to [publishing to production](https://github.com/grafana/website#publishing-to-production-grafanacom) if you'd like to do it yourself.
Generally, someone from marketing will publish to production each day: so as long as the sync is successful your docs edits will be published. Alternatively, you can refer to [publishing to production](https://github.com/grafana/website#publishing-to-production-grafanacom) if you'd like to do it yourself.
+1 -1
View File
@@ -112,7 +112,7 @@ grafana-cli --debug plugins install <plugin-id>
`--configOverrides` is a command line argument that acts like an environmental variable override.
For example, you can use it to redirect logging to another file (maybe to log plugin installations in Grafana Cloud) or when resetting the admin password and you have non-default values for some important config value (like where the database is located).
For example, you can use it to redirect logging to another file (maybe to log plugin installations in Grafana Cloud) or when resetting the admin password and you have non-default values for some important configuration value (like where the database is located).
**Example:**
```bash
@@ -1336,6 +1336,10 @@ Set to `true` if you want to test alpha plugins that are not yet ready for gener
Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature.
### marketplace_url
Custom install/learn more url for enterprise plugins. Defaults to https://grafana.com/grafana/plugins/.
<hr>
## [plugin.grafana-image-renderer]
+9 -2
View File
@@ -30,9 +30,9 @@ Check out the [configuration]({{< relref "configuration.md" >}}) page for more i
### Using Environment Variables
It is possible to use environment variable interpolation in all 3 provisioning config types. Allowed syntax
It is possible to use environment variable interpolation in all 3 provisioning configuration types. Allowed syntax
is either `$ENV_VAR_NAME` or `${ENV_VAR_NAME}` and can be used only for values not for keys or bigger parts
of the configs. It is not available in the dashboard's definition files just the dashboard provisioning
of the configurations. It is not available in the dashboard's definition files just the dashboard provisioning
configuration.
Example:
@@ -157,6 +157,11 @@ Since not all datasources have the same configuration settings we only have the
| interval | string | Elasticsearch | Index date time format. nil(No Pattern), 'Hourly', 'Daily', 'Weekly', 'Monthly' or 'Yearly' |
| logMessageField | string | Elasticsearch | Which field should be used as the log message |
| logLevelField | string | Elasticsearch | Which field should be used to indicate the priority of the log message |
| sigV4AuthType | string | Elasticsearch | SigV4 auth provider. default/credentials/keys |
| sigV4ExternalId | string | Elasticsearch | Optional SigV4 External ID |
| sigV4AssumeRoleArn | string | Elasticsearch | Optional SigV4 ARN role to assume |
| sigV4Region | string | Elasticsearch | SigV4 AWS region |
| sigV4Profile | string | Elasticsearch | Optional SigV4 credentials profile |
| authType | string | Cloudwatch | Auth provider. default/credentials/keys |
| externalId | string | Cloudwatch | Optional External ID |
| assumeRoleArn | string | Cloudwatch | Optional ARN role to assume |
@@ -191,6 +196,8 @@ Secure json data is a map of settings that will be encrypted with [secret key]({
| basicAuthPassword | string | _All_ | password for basic authentication |
| accessKey | string | Cloudwatch | Access key for connecting to Cloudwatch |
| secretKey | string | Cloudwatch | Secret key for connecting to Cloudwatch |
| sigV4AccessKey | string | Elasticsearch | SigV4 access key. Required when using keys auth provider |
| sigV4SecretKey | string | Elasticsearch | SigV4 secret key. Required when using keys auth provider |
#### Custom HTTP headers for datasources
+1 -1
View File
@@ -39,5 +39,5 @@ Users with the Viewer role can enter *any possible query* in *any* of the data s
To address this vulnerability, you can restrict data source query access in the following ways:
- Create multiple data sources with some restrictions added in data source config that restrict access (like database name or credentials). Then use the [Data Source Permissions]({{< relref "../permissions/datasource_permissions.md" >}}) Enterprise feature to restrict user access to the data source in Grafana.
- Create multiple data sources with some restrictions added in data source configuration that restrict access (like database name or credentials). Then use the [Data Source Permissions]({{< relref "../permissions/datasource_permissions.md" >}}) Enterprise feature to restrict user access to the data source in Grafana.
- Create a separate Grafana organization, and in that organization, create a separate data source. Make sure the data source has some option/user/credentials setting that limits access to a subset of the data. Not all data sources have an option to limit access.
+1 -1
View File
@@ -42,7 +42,7 @@ This section describes the fields you fill out to create an alert.
- **Evaluate every -** Specify how often the scheduler should evaluate the alert rule. This is referred to as the _evaluation interval_.
- **For -** Specify how long the query needs to violate the configured thresholds before the alert notification triggers.
You can set a minimum evaluation interval in the `alerting.min_interval_seconds` config field, to set a minimum time between evaluations. Refer to [Configuration]({{< relref "../administration/configuration.md" >}}#min-interval-seconds) for more information.
You can set a minimum evaluation interval in the `alerting.min_interval_seconds` configuration field, to set a minimum time between evaluations. Refer to [Configuration]({{< relref "../administration/configuration.md" >}}#min-interval-seconds) for more information.
> **Caution:** 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`.
+2 -3
View File
@@ -94,7 +94,7 @@ email = "email"
### Using environment variables
You can interpolate variables in the TOML config from environment variables. For instance, you could externalize your `bind_password` that way:
You can interpolate variables in the TOML configuration from environment variables. For instance, you could externalize your `bind_password` that way:
```bash
bind_password = "${LDAP_ADMIN_PASSWORD}"
@@ -161,8 +161,7 @@ In `[[servers.group_mappings]]` you can map an LDAP group to a Grafana organizat
the authoritative source. So, if you change a user's role in the Grafana Org. Users page, this change will be reset the next time the user logs in. If you
change the LDAP groups of a user, the change will take effect the next time the user logs in.
The first group mapping that an LDAP user is matched to will be used for the sync. If you have LDAP users that fit multiple mappings, the topmost mapping in the
TOML config will be used.
The first group mapping that an LDAP user is matched to will be used for the sync. If you have LDAP users that fit multiple mappings, the topmost mapping in the TOML configuration will be used.
**LDAP specific configuration file (ldap.toml) example:**
```bash
+14 -1
View File
@@ -10,7 +10,20 @@ weight = 600
# Share a panel
Click a panel title to open the panel menu, then click share in the panel menu to open the Share Panel dialog. Here you have access to a link that will take you to exactly this panel with the current time range and selected template variables. Below are ways to share a panel.
To share a panel
1. Click a panel title to open the panel menu.
1. Click share in the panel menu to open the Share Panel dialog.
Here you have access to a link that will take you to exactly this panel with the current time range and selected template variables. Here are some of the ways you can share a panel.
## Publish snapshots
You can publish snapshots to your local instance or to [snapshot.raintank.io](http://snapshot.raintank.io). The latter is a free service provided by [Raintank](http://raintank.io), that allows you to publish dashboard snapshots to an external Grafana instance.
Anyone with the link can view it. You can optionally set an expiration time if you want the snapshot to be removed after a certain time period.
{{< docs-imagebox img="/img/docs/panels/Share_a_panel.png" max-width="700px" >}}
## Direct Link Rendered Image
+1 -1
View File
@@ -47,7 +47,7 @@ The data source can access metrics from four different services. You can configu
- The Subscription Id can be changed per query. Save the data source and refresh the page to see the list of subscriptions available for the specified Client Id.
1. If you are also using the Azure Log Analytics service, then you need to specify these two config values (or you can reuse the Client Id and Secret from the previous step).
1. If you are also using the Azure Log Analytics service, then you need to specify these two configuration values (or you can reuse the Client Id and Secret from the previous step).
- Client Id (Azure Active Directory -> App Registrations -> Choose your app -> Application ID)
- Client Secret (Azure Active Directory -> App Registrations -> Choose your app -> Keys -> Create a key -> Use client secret)
+30 -1
View File
@@ -134,8 +134,37 @@ Note that Live Tailing relies on two Websocket connections: one between the brow
```
ProxyPassMatch "^/(api/datasources/proxy/\d+/loki/api/v1/tail)" "ws://127.0.0.1:3000/$1"
```
The following example shows basic NGINX proxy configuration. It assumes that the Grafana server is available at `http://localhost:3000/`, Loki server is running locally without proxy, and your external site uses HTTPS. If you also host Loki behind NGINX proxy, then you might want to repeat the following configuration for Loki as well.
> **Note:** This feature is only available in Grafana v6.3+
In the `http` section of NGINX configuration, add the following map definition:
```
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
```
In your `server` section, add the following configuration:
```
location ~ /(api/datasources/proxy/\d+/loki/api/v1/tail) {
proxy_pass http://localhost:3000$request_uri;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-for $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto "https";
proxy_set_header Connection $connection_upgrade;
proxy_set_header Upgrade $http_upgrade;
}
location / {
proxy_pass http://localhost:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-for $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto "https";
}
```
> **Note:** This feature is only available in Grafana v6.3+.
## Log Context
+1 -1
View File
@@ -83,4 +83,4 @@ Learn more about Grafana options and packages.
- [Grafana Plugin SDK for Go]({{< relref "backend/grafana-plugin-sdk-for-go" >}})
<!-- BEGIN Optimal Workshop Intercept Snippet --><div id='owInviteSnippet' style='position:fixed;right:20px;bottom:20px;width:280px;padding:20px;margin:0;border-radius:6px;background:#1857B8;color:#F7F8FA;text-align:left;z-index:2200000000;opacity:0;transition:opacity 500ms;-webkit-transition:opacity 500ms;display:none;'><div id='owInviteMessage' style='padding:0;margin:0 0 20px 0;font-size:16px;'>Got a spare two and a half minutes to help us improve the docs?</div><a id='owInviteOk' href='https://Grafana.optimalworkshop.com/questions/grafana-docs?tag=docs&utm_medium=intercept' onclick='this.parentNode.style.display="none";' target='_blank' style='color:#F7FAFF;font-size:16px;font-weight:bold;text-decoration:underline;'>Yes, I&#x27;ll help</a><a id='owInviteCancel' href='javascript:void(0)' onclick='this.parentNode.style.display="none";' style='color:#F7F8FA;font-size:14px;text-decoration:underline;float:right;'>Close</a></div><script>var owOnload=function(){if(-1==document.cookie.indexOf('ow-intercept-quiz-4ior230e')){var o=new XMLHttpRequest;o.onloadend=function(){try{var o=document.getElementById('owInviteSnippet');var date=new Date();date.setMonth(date.getMonth()+1);this.response&&JSON.parse(this.response).active===!0&&(document.cookie='ow-intercept-quiz-4ior230e=Done;path=/;expires='+date.toUTCString()+';',setTimeout(function(){o.style.display='block',o.style.opacity=1},2e3))}catch(e){}},o.open('POST','https://app.optimalworkshop.com/survey_status/questions/4ior230e/active'),o.send()}};if(window.addEventListener){window.addEventListener('load',function(){owOnload();});}else if(window.attachEvent){window.attachEvent('onload',function(){owOnload();});}</script><!-- END Optimal Workshop snippet -->
<!-- BEGIN Optimal Workshop Intercept Snippet --><div id='owInviteSnippet' style='position:fixed;right:20px;bottom:20px;width:280px;padding:20px;margin:0;border-radius:6px;background:#1857B8;color:#F7F8FA;text-align:left;z-index:2200000000;opacity:0;transition:opacity 500ms;-webkit-transition:opacity 500ms;display:none;'><div id='owInviteMessage' style='padding:0;margin:0 0 20px 0;font-size:16px;'>Got a spare two and a half minutes to help us improve the docs?</div><a id='owInviteOk' href='https://Grafana.optimalworkshop.com/questions/grafana-docs?tag=docs&utm_medium=intercept' onclick='this.parentNode.style.display="none";' target='_blank' style='color:#F7FAFF;font-size:16px;font-weight:bold;text-decoration:underline;'>Yes, I&#x27;ll help</a><a id='owInviteCancel' href='javascript:void(0)' onclick='this.parentNode.style.display="none";' style='color:#F7F8FA;font-size:14px;text-decoration:underline;float:right;'>Close</a></div><script>var owOnload=function(){if(-1==document.cookie.indexOf('ow-intercept-quiz-4ior230e')){var o=new XMLHttpRequest;o.onloadend=function(){try{var o=document.getElementById('owInviteSnippet');var date=new Date();date.setMonth(date.getMonth()+1);this.response&&JSON.parse(this.response).active===!0&&(document.cookie='ow-intercept-quiz-4ior230e=Done;path=/;expires='+date.toUTCString()+';',setTimeout(function(){o.style.display='block',o.style.opacity=1},2e3))}catch(e){}},o.open('POST','https://app.optimalworkshop.com/survey_status/questions/4ior230e/active'),o.send()}};if(window.addEventListener){window.addEventListener('load',function(){owOnload();});}else if(window.attachEvent){window.attachEvent('onload',function(){owOnload();});}</script><!-- END Optimal Workshop snippet -->
@@ -15,7 +15,7 @@ Data source and panel plugins will show up like normal plugins. The app pages wi
## Enabling app plugins
After installing an app, it has to be enabled before it shows up as a data source or panel. You can do that on the app page in the config tab.
After installing an app, it has to be enabled before it shows up as a data source or panel. You can do that on the app page in the configuration tab.
## Developing an App Plugin
@@ -135,7 +135,7 @@ Use the `width-x` and `max-width-x` classes to control the width of your labels
## Data Sources
For more information about data sources, refer to the [basic guide for data sources](http://docs.grafana.org/plugins/developing/datasources/).
### Config Page Guidelines
### Configuration Page Guidelines
- It should be as easy as possible for a user to configure a URL. If the data source is using the `datasource-http-settings` component, it should use the `suggest-url` attribute to suggest the default URL or a URL that is similar to what it should be (especially important if the URL refers to a REST endpoint that is not common knowledge for most users e.g. `https://yourserver:4000/api/custom-endpoint`).
@@ -155,7 +155,7 @@ If possible, any passwords or secrets should be saved in the `secureJsonData` bl
Read more here about how [authentication for data sources]({{< relref "../add-authentication-for-data-source-plugins.md" >}}) works.
If using the proxy feature, the Config page should use the `secureJsonData` blob like this:
If using the proxy feature, the Configuration page should use the `secureJsonData` blob like this:
- good: `<input type="password" class="gf-form-input" ng-model='ctrl.current.secureJsonData.password' placeholder="password"></input>`
- bad: `<input type="password" class="gf-form-input" ng-model='ctrl.current.password' placeholder="password"></input>`
@@ -46,7 +46,7 @@ For plugins prior to Grafana 7.0, all options are considered _Display options_.
While backend plugins were available as an experimental feature in previous versions of Grafana, the support has been greatly improved for Grafana 7. Backend plugins for Grafana 7.0 are backwards-compatible and will continue to work. However, the old backend plugin system has been deprecated, and we recommend that you use the new SDK for backend plugins.
Since Grafana 7.0 introduced [signing of backend plugins]({{< relref "../../plugins/plugin-signature-verification.md" >}}), community plugins wont load by default if theyre unsigned.
Since Grafana 7.0 introduced [signing of backend plugins]({{< relref "../../plugins/plugin-signatures.md" >}}), community plugins wont load by default if theyre unsigned.
To learn more, refer to [Backend plugins]({{< relref "backend" >}}).
@@ -127,7 +127,7 @@ export const MyPanel: React.FC<Props> = ({ options, data, width, height }) => {
While all plugins are different, we'd like to share a migration process that has worked for some of our users.
1. Define your configuration model and `ConfigEditor`. For many plugins, the config editor is the simplest component so it's a good candidate to start with.
1. Define your configuration model and `ConfigEditor`. For many plugins, the configuration editor is the simplest component so it's a good candidate to start with.
1. Implement the `testDatasource()` method on the class that extends `DataSourceApi` using the settings in your configuration model to make sure you can successfully configure and access the external API.
1. Implement the `query()` method. At this point, you can hard-code your query, because we havent yet implemented the query editor. The `query()` method supports both the new data frame response and the old TimeSeries response, so dont worry about converting to the new format just yet.
1. Implement the `QueryEditor`. How much work this requires depends on how complex your query model is.
@@ -165,4 +165,4 @@ For more information, refer to [Data frames](https://grafana.com/docs/grafana/la
## Troubleshoot plugin migration
With Grafana 7.0, backend plugins can now be cryptographically signed to verify their origin. By default, Grafana will ignore unsigned plugins. For more information, refer to [Allow unsigned plugins]({{< relref "../../plugins/plugin-signature-verification.md#allow-unsigned-plugins" >}}).
With Grafana 7.0, backend plugins can now be cryptographically signed to verify their origin. By default, Grafana will ignore unsigned plugins. For more information, refer to [Allow unsigned plugins]({{< relref "../../plugins/plugin-signatures.md#allow-unsigned-plugins" >}}).
@@ -0,0 +1,47 @@
+++
title = "Package a plugin"
type = "docs"
aliases = ["/docs/grafana/latest/developers/plugins/share-a-plugin/"]
+++
# Package a plugin
You've just built your first plugin, and now you want to share it with the world. In this guide, you'll learn how to package and share your plugin with others.
For Grafana to be able to load a plugin, it first needs to be built. When you build a plugin from source, a `dist` directory is created that contains the production build, or _plugin assets_, for your plugin.
When the Grafana server starts, it recursively looks in the plugin directory for any directory that contains a `plugin.json` file and tries to load the plugin assets in the same directory.
There are three steps needed to package a plugin:
- Building the plugin
- Signing the plugin
- Archiving the plugin
1. Build the plugin
```
yarn install --pure-lockfile
yarn build
```
1. (Optional) If your data source plugin has a backend plugin, build it as well.
```
mage
```
1. [Sign the plugin]({{< relref "sign-a-plugin.md" >}}).
1. Create a ZIP archive of the `dist` directory.
```
mv dist/ myorg-simple-panel
zip myorg-simple-panel-1.0.0.zip myorg-simple-panel -r
```
## Publish your plugin on Grafana.com
The best way to share your plugin with the world is to publish it on [Grafana Plugins](https://grafana.com/plugins). By having your plugin published on Grafana.com, more users will be able to discover your plugin.
To publish a plugin to [Grafana Plugins](https://grafana.com/grafana/plugins), create a pull request to the [Grafana Plugin Repository](https://github.com/grafana/grafana-plugin-repository). Please note that both the source code and the packaged plugin archive need to be publicly available.
@@ -1,120 +0,0 @@
+++
title = "Share a plugin"
type = "docs"
+++
# Share a plugin
You've just built your first plugin, and now you want to share it with the world. In this guide, you'll learn how to package and share your plugin with others.
When you build a plugin from source, a `dist` directory is created that contains the production build, or _plugin assets_, for your plugin.
When loading your plugin, Grafana only cares about the plugin assets. Specifically, when the Grafana server starts, it attempts to discover and load plugins like this:
1. Look for a `plugin.json` file in any of the subdirectories in the plugin directory.
1. If a `plugin.json` was found, try to load the plugin assets from a `dist` directory in the same directory as the `plugin.json` file.
1. If there's no `dist` directory, try to load the plugin assets from the same directory as the `plugin.json` file.
Now that you know what Grafana needs to load your plugin, let's see how you can share the plugin with other users.
The best way to share your plugin with the world is to publish it on [Grafana Plugins](https://grafana.com/plugins). However, if you're not ready to make your plugin public just yet, you can still share your plugin by hosting the plugin yourself.
## Publish your plugin on Grafana.com
To publish a plugin to [Grafana Plugins](https://grafana.com/grafana/plugins), your plugin first needs to be publicly available in a commit on [GitHub](https://github.com).
The commit you submit needs to either:
- Contain a `dist` directory with the plugin assets
- Contain the plugin assets in the root directory
We strongly recommend that you don't check in the plugin assets to the main branch. Instead, use the following steps to create a release branch that contains the plugin assets.
1. Create a release branch.
```
git checkout -b release-0.1.x
```
1. Build the plugin assets.
```
yarn build
```
1. Add the `dist` directory. The `-f` flag adds the directory even if it's ignored by `.gitignore`.
```
git add -f dist
```
1. Create the release commit.
```
git commit -m "Release v0.1.0"
```
1. Create a release tag. You can also [create the release on GitHub](https://docs.github.com/en/github/administering-a-repository/managing-releases-in-a-repository). If you do, then you can skip this step and the next one.
```
git tag -a v0.1.0 -m "Create release tag v0.1.0"
```
1. Push to GitHub. `follow-tags` tells Git to push the release tag along with our release branch.
```
git push --set-upstream origin release-0.1.x --follow-tags
```
The next step is to submit the URL to your repository, and the release commit, to the [Grafana Plugin Repository](https://github.com/grafana/grafana-plugin-repository).
## Host the plugin yourself
If you want to share your plugin by hosting it yourself, then we recommend that you package it by adding the plugin assets to a .zip archive. You can then make the archive available by hosting it yourself.
How you package the plugin depends on whether you want to include the source code or not.
### Package the plugin with source code
If you want to distribute the source code along with your plugin assets, then you can archive the entire plugin directory.
To create a .zip archive that contains the plugin assets and source code, run the following commands in your terminal:
```
cd my-plugin/
yarn build
zip my-plugin-0.2.0.src.zip . -r -x "node_modules/*" -x ".git/*"
```
### Package the plugin without source code
If you don't want to distribute the plugin with the source code, then you can archive the `dist` directory.
To create a .zip archive that only contains the bare minimum to load the plugin, run the following commands in your terminal:
```
cd my-plugin/
yarn build
cd dist/
zip my-plugin-0.2.0.nosrc.zip . -r
```
### Package and host your plugin using GitHub
If you host your plugin on GitHub, then you can share the plugin using the following URL:
```
https://github.com/GITHUB_USERNAME/GITHUB_REPO_NAME/archive/<VERSION>.zip
```
For example, you can download the [Worldmap Panel](https://github.com/grafana/worldmap-panel) using the following URL:
[https://github.com/grafana/worldmap-panel/archive/v0.3.2.zip](https://github.com/grafana/worldmap-panel/archive/v0.3.2.zip)
### Install a packaged plugin
After the user has downloaded the archive containing the plugin assets, they can install it by extracting the archive into their plugin directory.
```
unzip my-plugin-0.2.0.zip -d YOUR_PLUGIN_DIR/my-plugin
```
@@ -5,6 +5,97 @@ type = "docs"
# Sign a plugin
Signing a plugin allows Grafana to verify the authenticity of the plugin with [signature verification]({{< relref "../../plugins/plugin-signature-verification.md" >}}). This gives users a way to make sure plugins haven't been tampered with. All Grafana Labs-authored backend plugins, including Enterprise plugins, are signed.
Signing a plugin allows Grafana to verify the authenticity of the plugin with [signature verification]({{< relref "../../plugins/plugin-signatures.md" >}}). This gives users a way to make sure plugins haven't been tampered with. All Grafana Labs-authored backend plugins, including Enterprise plugins, are signed.
We're looking into providing a process for allowing community plugins to be signed in an upcoming version of Grafana.
> **Important:** Future versions of Grafana will require all plugins to be signed.
## Sign your plugin using Grafana Toolkit
The easiest way to sign your plugin is by using the [Grafana Toolkit](https://www.npmjs.com/package/@grafana/toolkit).
You can sign your plugin as a _public_ or a _private_ plugin. In both cases, you need to [create an account on Grafana.com](https://grafana.com/signup) and generate an API key with the `PluginPublisher` role. By creating an account, you can verify that you own the plugin that you want to sign.
### Sign a public plugin
Plugins signed under the community or commercial signature level are considered _public plugins_. Public plugins are published on [Grafana Plugin](https://grafana.com/plugins). For more information about installing public plugins, refer to [Install Grafana plugins]({{< relref "../../plugins/installation.md" >}}).
1. Request a plugin signature level by sending an email to [plugins@grafana.com](mailto:plugins@grafana.com).
1. Sign the plugin with the API key you just created. Grafana Toolkit creates a [MANIFEST.txt](#plugin-manifest) file in the `dist` directory of your plugin.
```
export GRAFANA_API_KEY=<YOUR_API_KEY>
npx @grafana/toolkit plugin:sign
```
### Sign a private plugin
If you're developing plugins for internal use only and don't want to make it public, you can sign it under a Private [signature level](#plugin-signature-levels).
1. Sign the plugin with the API key you just created. Grafana Toolkit creates a [MANIFEST.txt](#plugin-manifest) file in the `dist` directory of your plugin.
The `rootUrls` flag accepts a comma-separated list of URLs for which the plugin can be used. The URLs need to match the [root_url]({{< relref "../../administration/configuration.md#root_url" >}}) setting.
```
export GRAFANA_API_KEY=<YOUR_API_KEY>
npx @grafana/toolkit plugin:sign --rootUrls https://example.com/grafana
```
## Plugin signature levels
To sign a plugin, you need to decide the _signature level_ you want to sign it under. The signature level of your plugin determines how you can distribute it.
You can sign your plugin under three different _signature levels_.
|**Plugin Level**|**Paid Subscription Required?**|**Description**|
|---|---|---|
|Private|No;<br>Free of charge|<p>You can create and sign a Private Plugin for any technology at no charge.</p><p>Private Plugins are for use on your own Grafana. They may not be distributed to the Grafana community, and are not published in the Grafana catalog.</p>|
|Community|No;<br>Free of charge|<p>You can create, sign and distribute plugins at no charge, provided that all dependent technologies are open source and not for profit.</p><p>Community Plugins are published in the official Grafana catalog, and are available to the Grafana community.</p>|
|Commercial|Yes;<br>Commercial Plugin Subscription required|<p>You can create, sign and distribute plugins with dependent technologies that are closed source or commercially backed, by entering into a Commercial Plugin Subscription with Grafana Labs.</p><p>Commercial Plugins are published on the official Grafana catalog, and are available to the Grafana community.</p>|
For instructions on how to sign a plugin under the Community and Commercial signature level, refer to [Sign a public plugin](#sign-a-public-plugin).
For instructions on how to sign a plugin under the Private signature level, refer to [Sign a private plugin](#sign-a-private-plugin).
## Plugin manifest
For Grafana to verify the digital signature of a plugin, the plugin must include a signed manifest file, _MANIFEST.txt_. The signed manifest file contains two sections:
- **Signed message -** The signed message contains plugin metadata and plugin files with their respective checksums (SHA256).
- **Digital signature -** The digital signature is created by encrypting the signed message using a private key. Grafana has a public key built-in that can be used to verify that the digital signature have been encrypted using expected private key.
**Example manifest file:**
```txt
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
{
"manifestVersion": "2.0.0",
"signatureType": "community",
"signedByOrg": "myorgid",
"signedByOrgName": "My Org",
"plugin": "myorgid-simple-panel",
"version": "1.0.0",
"time": 1602753404133,
"keyId": "7e4d0c6a708866e7",
"files": {
"LICENSE": "12ab7a0961275f5ce7a428e662279cf49bab887d12b2ff7bfde738346178c28c",
"module.js.LICENSE.txt": "0d8f66cd4afb566cb5b7e1540c68f43b939d3eba12ace290f18abc4f4cb53ed0",
"module.js.map": "8a4ede5b5847dec1c6c30008d07bef8a049408d2b1e862841e30357f82e0fa19",
"plugin.json": "13be5f2fd55bee787c5413b5ba6a1fae2dfe8d2df6c867dadc4657b98f821f90",
"README.md": "2d90145b28f22348d4f50a81695e888c68ebd4f8baec731fdf2d79c8b187a27f",
"module.js": "b4b6945bbf3332b08e5e1cb214a5b85c82557b292577eb58c8eb1703bc8e4577"
}
}
-----BEGIN PGP SIGNATURE-----
Version: OpenPGP.js v4.10.1
Comment: https://openpgpjs.org
wqEEARMKAAYFAl+IE3wACgkQfk0ManCIZudpdwIHTCqjVzfm7DechTa7BTbd
+dNIQtwh8Tv2Q9HksgN6c6M9nbQTP0xNHwxSxHOI8EL3euz/OagzWoiIWulG
7AQo7FYCCQGucaLPPK3tsWaeFqVKy+JtQhrJJui23DAZLSYQYZlKQ+nFqc9x
T6scfmuhWC/TOcm83EVoCzIV3R5dOTKHqkjIUg==
=GdNq
-----END PGP SIGNATURE-----
```
+4 -2
View File
@@ -59,17 +59,19 @@ With Grafana Enterprise, you get access to new features, including:
With a Grafana Enterprise license, you get access to premium plugins, including:
- [Amazon Timestream](https://grafana.com/plugins/grafana-timestream-datasource)
- [AppDynamics](https://grafana.com/plugins/dlopes7-appdynamics-datasource)
- [DataDog](https://grafana.com/plugins/grafana-datadog-datasource)
- [Dynatrace](https://grafana.com/plugins/grafana-dynatrace-datasource)
- [MongoDB](https://grafana.com/grafana/plugins/grafana-mongodb-datasource)
- [New Relic](https://grafana.com/plugins/grafana-newrelic-datasource)
- [Oracle Database](https://grafana.com/plugins/grafana-oracle-datasource)
- [ServiceNow](https://grafana.com/grafana/plugins/grafana-servicenow-datasource)
- [Snowflake](https://grafana.com/grafana/plugins/grafana-snowflake-datasource)
- [Splunk](https://grafana.com/plugins/grafana-splunk-datasource)
- [Wavefront](https://grafana.com/grafana/plugins/grafana-wavefront-datasource)
## Try Grafana Enterprise
To purchase or obtain a trial license contact the Grafana Labs [Sales Team](https://grafana.com/contact?about=support&topic=Grafana%20Enterprise).
<!-- BEGIN Optimal Workshop Intercept Snippet --><div id='owInviteSnippet' style='position:fixed;right:20px;bottom:20px;width:280px;padding:20px;margin:0;border-radius:6px;background:#1857B8;color:#F7F8FA;text-align:left;z-index:2200000000;opacity:0;transition:opacity 500ms;-webkit-transition:opacity 500ms;display:none;'><div id='owInviteMessage' style='padding:0;margin:0 0 20px 0;font-size:16px;'>Got a spare two and a half minutes to help us improve the docs?</div><a id='owInviteOk' href='https://Grafana.optimalworkshop.com/questions/grafana-docs?tag=docs&utm_medium=intercept' onclick='this.parentNode.style.display="none";' target='_blank' style='color:#F7FAFF;font-size:16px;font-weight:bold;text-decoration:underline;'>Yes, I&#x27;ll help</a><a id='owInviteCancel' href='javascript:void(0)' onclick='this.parentNode.style.display="none";' style='color:#F7F8FA;font-size:14px;text-decoration:underline;float:right;'>Close</a></div><script>var owOnload=function(){if(-1==document.cookie.indexOf('ow-intercept-quiz-4ior230e')){var o=new XMLHttpRequest;o.onloadend=function(){try{var o=document.getElementById('owInviteSnippet');var date=new Date();date.setMonth(date.getMonth()+1);this.response&&JSON.parse(this.response).active===!0&&(document.cookie='ow-intercept-quiz-4ior230e=Done;path=/;expires='+date.toUTCString()+';',setTimeout(function(){o.style.display='block',o.style.opacity=1},2e3))}catch(e){}},o.open('POST','https://app.optimalworkshop.com/survey_status/questions/4ior230e/active'),o.send()}};if(window.addEventListener){window.addEventListener('load',function(){owOnload();});}else if(window.attachEvent){window.attachEvent('onload',function(){owOnload();});}</script><!-- END Optimal Workshop snippet -->
<!-- BEGIN Optimal Workshop Intercept Snippet --><div id='owInviteSnippet' style='position:fixed;right:20px;bottom:20px;width:280px;padding:20px;margin:0;border-radius:6px;background:#1857B8;color:#F7F8FA;text-align:left;z-index:2200000000;opacity:0;transition:opacity 500ms;-webkit-transition:opacity 500ms;display:none;'><div id='owInviteMessage' style='padding:0;margin:0 0 20px 0;font-size:16px;'>Got a spare two and a half minutes to help us improve the docs?</div><a id='owInviteOk' href='https://Grafana.optimalworkshop.com/questions/grafana-docs?tag=docs&utm_medium=intercept' onclick='this.parentNode.style.display="none";' target='_blank' style='color:#F7FAFF;font-size:16px;font-weight:bold;text-decoration:underline;'>Yes, I&#x27;ll help</a><a id='owInviteCancel' href='javascript:void(0)' onclick='this.parentNode.style.display="none";' style='color:#F7F8FA;font-size:14px;text-decoration:underline;float:right;'>Close</a></div><script>var owOnload=function(){if(-1==document.cookie.indexOf('ow-intercept-quiz-4ior230e')){var o=new XMLHttpRequest;o.onloadend=function(){try{var o=document.getElementById('owInviteSnippet');var date=new Date();date.setMonth(date.getMonth()+1);this.response&&JSON.parse(this.response).active===!0&&(document.cookie='ow-intercept-quiz-4ior230e=Done;path=/;expires='+date.toUTCString()+';',setTimeout(function(){o.style.display='block',o.style.opacity=1},2e3))}catch(e){}},o.open('POST','https://app.optimalworkshop.com/survey_status/questions/4ior230e/active'),o.send()}};if(window.addEventListener){window.addEventListener('load',function(){owOnload();});}else if(window.attachEvent){window.attachEvent('onload',function(){owOnload();});}</script><!-- END Optimal Workshop snippet -->
+1 -1
View File
@@ -185,7 +185,7 @@ Organization mapping allows you to assign users to particular organization in Gr
1. In configuration file, set [`assertion_attribute_org`]({{< relref "./enterprise-configuration.md#assertion-attribute-org" >}}) to the attribute name you store organization info in.
1. Set [`org_mapping`]({{< relref "./enterprise-configuration.md#org-mapping" >}}) option to the comma-separated list of `Organization:OrgId` pairs to map organization from IdP to Grafana organization specified by id.
For example, use following config to assign users from `Engineering` organization to the Grafana organization with id `2` and users from `Sales` - to the org with id `3`, based on `Org` assertion attribute value:
For example, use following configuration to assign users from `Engineering` organization to the Grafana organization with id `2` and users from `Sales` - to the org with id `3`, based on `Org` assertion attribute value:
```bash
[auth.saml]
+18
View File
@@ -55,3 +55,21 @@ It shows two kinds of information:
In the search view, you can sort dashboards using these insights data. It helps you find unused or broken dashboards or discover most viewed ones.
{{< docs-imagebox img="/img/docs/enterprise/improved_search.png" max-width="650px" class="docs-image--no-shadow" >}}
## Data source insights
> Only available in Grafana Enterprise v7.3+.
Data source insights give you information about how a data source has been used in the last thirty days.
- Queries per day
- Errors per day
- Average load duration per day (ms)
To find data source insights, go to:
1. Data source list view
1. Click on a data source
1. Click the insights tab
{{< docs-imagebox img="/img/docs/enterprise/datasource_insights.png" max-width="650px" class="docs-image--no-shadow" >}}
+6 -2
View File
@@ -145,7 +145,7 @@ Suggestions can appear under the query field - click on them to update your quer
### Table filters
Click on the filter button <span title="Filter for label" class="logs-label__icon fa fa-search-plus"></span> in a labels column in the Table panel to add filters to the query expression. This works with multiple queries too - the filter will be added for all the queries.
Click on the filter button in the "label" column of a Table panel to add filters to the query expression. You can add filters for multiple queries as well - the filter is added for all the queries.
## Logs integration
@@ -155,6 +155,10 @@ Along with metrics, Explore allows you to investigate your logs with the followi
- [InfluxDB](../datasources/influxdb)
- [Elasticsearch](../datasources/elasticsearch)
### Logs visualization
Results of log queries are shown as histograms in the graph and individual logs are displayed below. If the data source does not send histogram data for the requested time range, the logs model computes a time series based on the log row counts bucketed by an automatically calculated time interval and the start of the histogram is then anchored by the first log row's timestamp from the result. The end of the time series is anchored to the time picker's **To** range.
### Visualization options
You can customize how logs are displayed and select which columns are shown.
@@ -297,4 +301,4 @@ This functionality is similar to the panel inspector [Stats tab]({{< relref "../
{{< docs-imagebox img="/img/docs/v71/query_inspector_explore.png" class="docs-image--no-shadow" caption="Screenshot of the query inspector button in Explore" >}}
<!-- BEGIN Optimal Workshop Intercept Snippet --><div id='owInviteSnippet' style='position:fixed;right:20px;bottom:20px;width:280px;padding:20px;margin:0;border-radius:6px;background:#1857B8;color:#F7F8FA;text-align:left;z-index:2200000000;opacity:0;transition:opacity 500ms;-webkit-transition:opacity 500ms;display:none;'><div id='owInviteMessage' style='padding:0;margin:0 0 20px 0;font-size:16px;'>Got a spare two and a half minutes to help us improve the docs?</div><a id='owInviteOk' href='https://Grafana.optimalworkshop.com/questions/grafana-docs?tag=docs&utm_medium=intercept' onclick='this.parentNode.style.display="none";' target='_blank' style='color:#F7FAFF;font-size:16px;font-weight:bold;text-decoration:underline;'>Yes, I&#x27;ll help</a><a id='owInviteCancel' href='javascript:void(0)' onclick='this.parentNode.style.display="none";' style='color:#F7F8FA;font-size:14px;text-decoration:underline;float:right;'>Close</a></div><script>var owOnload=function(){if(-1==document.cookie.indexOf('ow-intercept-quiz-4ior230e')){var o=new XMLHttpRequest;o.onloadend=function(){try{var o=document.getElementById('owInviteSnippet');var date=new Date();date.setMonth(date.getMonth()+1);this.response&&JSON.parse(this.response).active===!0&&(document.cookie='ow-intercept-quiz-4ior230e=Done;path=/;expires='+date.toUTCString()+';',setTimeout(function(){o.style.display='block',o.style.opacity=1},2e3))}catch(e){}},o.open('POST','https://app.optimalworkshop.com/survey_status/questions/4ior230e/active'),o.send()}};if(window.addEventListener){window.addEventListener('load',function(){owOnload();});}else if(window.attachEvent){window.attachEvent('onload',function(){owOnload();});}</script><!-- END Optimal Workshop snippet -->
<!-- BEGIN Optimal Workshop Intercept Snippet --><div id='owInviteSnippet' style='position:fixed;right:20px;bottom:20px;width:280px;padding:20px;margin:0;border-radius:6px;background:#1857B8;color:#F7F8FA;text-align:left;z-index:2200000000;opacity:0;transition:opacity 500ms;-webkit-transition:opacity 500ms;display:none;'><div id='owInviteMessage' style='padding:0;margin:0 0 20px 0;font-size:16px;'>Got a spare two and a half minutes to help us improve the docs?</div><a id='owInviteOk' href='https://Grafana.optimalworkshop.com/questions/grafana-docs?tag=docs&utm_medium=intercept' onclick='this.parentNode.style.display="none";' target='_blank' style='color:#F7FAFF;font-size:16px;font-weight:bold;text-decoration:underline;'>Yes, I&#x27;ll help</a><a id='owInviteCancel' href='javascript:void(0)' onclick='this.parentNode.style.display="none";' style='color:#F7F8FA;font-size:14px;text-decoration:underline;float:right;'>Close</a></div><script>var owOnload=function(){if(-1==document.cookie.indexOf('ow-intercept-quiz-4ior230e')){var o=new XMLHttpRequest;o.onloadend=function(){try{var o=document.getElementById('owInviteSnippet');var date=new Date();date.setMonth(date.getMonth()+1);this.response&&JSON.parse(this.response).active===!0&&(document.cookie='ow-intercept-quiz-4ior230e=Done;path=/;expires='+date.toUTCString()+';',setTimeout(function(){o.style.display='block',o.style.opacity=1},2e3))}catch(e){}},o.open('POST','https://app.optimalworkshop.com/survey_status/questions/4ior230e/active'),o.send()}};if(window.addEventListener){window.addEventListener('load',function(){owOnload();});}else if(window.attachEvent){window.attachEvent('onload',function(){owOnload();});}</script><!-- END Optimal Workshop snippet -->
+24
View File
@@ -40,12 +40,36 @@ This topic lists words and abbreviations that are commonly used in the Grafana d
Explore allows a user to focus on a building a query. User can refine the query to return the expected metrics before building a dashboard.
</td>
</tr>
<tr>
<td style="vertical-align: top">Export/Import dashboard</td>
<td>
Grafana includes the ability to export your dashboards to a file containing JSON. Community members sometimes share their created dashboards on the [Grafana Dashboards page](https://grafana.com/grafana/dashboards). Dashboards previously exported or found on this site may be imported by other users.
</td>
</tr>
<tr>
<td style="vertical-align: top">Exporter</td>
<td>
An exporter translates data that comes out of a data source into a format that Prometheus can digest.
</td>
</tr>
<tr>
<td style="vertical-align: top">Integration (Grafana Cloud)</td>
<td>
Each Integration in Grafana Cloud uses the cloud agent to connect your data source to Grafana Cloud for visualizing. Note: Prometheus uses the word “integrations” to refer to software that exposes Prometheus metrics without needing an exporter, which is a different use of the same word we use here.
</td>
</tr>
<tr>
<td style="vertical-align: top">Graph</td>
<td>
A commonly-used visualization that displays data as points, lines, or bars.
</td>
</tr>
<tr>
<td style="vertical-align: top">Mixin</td>
<td>
A mixin is a set of Grafana dashboards and Prometheus rules and alerts, written in Jsonnet and packaged together in a bundle.
</td>
</tr>
<tr>
<td style="vertical-align: top">Panel</td>
<td>
+1 -1
View File
@@ -471,7 +471,7 @@ Content-Type: application/json
Reloads the provisioning config files for specified type and provision entities again. It won't return
until the new provisioned entities are already stored in the database. In case of dashboards, it will stop
polling for changes in dashboard files and then restart it with new configs after returning.
polling for changes in dashboard files and then restart it with new configurations after returning.
Only works with Basic Authentication (username and password). See [introduction](http://docs.grafana.org/http_api/admin/#admin-api) for an explanation.
+1 -1
View File
@@ -309,7 +309,7 @@ Content-Type: application/json
```
Note: The api will work in the following two ways
1) Need to set GF_USERS_ALLOW_ORG_CREATE=true
2) Set the config users.allow_org_create to true in ini file
2) Set the config value users.allow_org_create to true in ini file
**Example Response**:
+21 -4
View File
@@ -21,7 +21,7 @@ Upgrading is generally safe (between many minor and one major version) and dashb
We recommend that you backup a few things in case you have to rollback the upgrade.
- Installed plugins - Back them up before you upgrade them in case you want to rollback the Grafana version and want to get the exact same versions you were running before the upgrade.
- Configuration files do not need to be backed up. However, you might want to in case you add new config options after upgrade and then rollback.
- Configuration files do not need to be backed up. However, you might want to in case you add new configuration options after upgrade and then rollback.
### Database backup
@@ -82,7 +82,7 @@ sudo apt-get upgrade
If you downloaded the binary `.tar.gz` package, then you can just download and extract the new package and overwrite all your existing files. However, this might overwrite your config changes.
We recommend that you save your custom config changes in a file named `<grafana_install_dir>/conf/custom.ini`.
We recommend that you save your custom configuration changes in a file named `<grafana_install_dir>/conf/custom.ini`.
This allows you to upgrade Grafana without risking losing your configuration changes.
### Centos / RHEL
@@ -108,7 +108,7 @@ docker run -d --name=my-grafana-container --restart=always -v /var/lib/grafana:/
### Windows
If you downloaded the Windows binary package you can just download a newer package and extract to the same location (and overwrite the existing files). This might overwrite your config changes. We recommend that you save your config changes in a file named `<grafana_install_dir>/conf/custom.ini` as this will make upgrades easier without risking losing your config changes.
If you downloaded the Windows binary package you can just download a newer package and extract to the same location (and overwrite the existing files). This might overwrite your configuration changes. We recommend that you save your configuration changes in a file named `<grafana_install_dir>/conf/custom.ini` as this will make upgrades easier without risking losing your configuration changes.
## Update plugins
@@ -297,4 +297,21 @@ The other authentication methods, _Access & secret key_ and _Credentials file_,
For more information and details, please refer to [Using AWS CloudWatch in Grafana]({{< relref "../datasources/cloudwatch.md#authentication" >}}).
<!-- BEGIN Optimal Workshop Intercept Snippet --><div id='owInviteSnippet' style='position:fixed;right:20px;bottom:20px;width:280px;padding:20px;margin:0;border-radius:6px;background:#1857B8;color:#F7F8FA;text-align:left;z-index:2200000000;opacity:0;transition:opacity 500ms;-webkit-transition:opacity 500ms;display:none;'><div id='owInviteMessage' style='padding:0;margin:0 0 20px 0;font-size:16px;'>Got a spare two and a half minutes to help us improve the docs?</div><a id='owInviteOk' href='https://Grafana.optimalworkshop.com/questions/grafana-docs?tag=docs&utm_medium=intercept' onclick='this.parentNode.style.display="none";' target='_blank' style='color:#F7FAFF;font-size:16px;font-weight:bold;text-decoration:underline;'>Yes, I&#x27;ll help</a><a id='owInviteCancel' href='javascript:void(0)' onclick='this.parentNode.style.display="none";' style='color:#F7F8FA;font-size:14px;text-decoration:underline;float:right;'>Close</a></div><script>var owOnload=function(){if(-1==document.cookie.indexOf('ow-intercept-quiz-4ior230e')){var o=new XMLHttpRequest;o.onloadend=function(){try{var o=document.getElementById('owInviteSnippet');var date=new Date();date.setMonth(date.getMonth()+1);this.response&&JSON.parse(this.response).active===!0&&(document.cookie='ow-intercept-quiz-4ior230e=Done;path=/;expires='+date.toUTCString()+';',setTimeout(function(){o.style.display='block',o.style.opacity=1},2e3))}catch(e){}},o.open('POST','https://app.optimalworkshop.com/survey_status/questions/4ior230e/active'),o.send()}};if(window.addEventListener){window.addEventListener('load',function(){owOnload();});}else if(window.attachEvent){window.attachEvent('onload',function(){owOnload();});}</script><!-- END Optimal Workshop snippet -->
### User invites database migration
The database table _temp\_user_, that tracks user invites, is subject to a database migration that changes the data type of the _created_ and _updated_ columns:
| Database | Old data type | New data type |
| -------- | ------------- | ------------- |
| Sqlite | DATETIME | INTEGER |
| MySQL | DATETIME | INT |
| Postgres | TIMESTAMP | INTEGER |
> Please note that if downgrading Grafana to an earlier version, you have to manually change the data type of the _created_ and _updated_ columns back to _old data type_ , otherwise the user invite feature doesn't function as expected.
### Snapshots database migration
The database table _dashboard\_snapshot_, that stores dashboard snapshots, adds a new column _dashboard\_encrypted_ for storing an encrypted snapshot.
NOTE: Only snapshots created on Grafana 7.3 or later will use this column to store snapshot data as encrypted. Snapshots created before this version will be unaffected and remain unencrypted.
<!-- BEGIN Optimal Workshop Intercept Snippet --><div id='owInviteSnippet' style='position:fixed;right:20px;bottom:20px;width:280px;padding:20px;margin:0;border-radius:6px;background:#1857B8;color:#F7F8FA;text-align:left;z-index:2200000000;opacity:0;transition:opacity 500ms;-webkit-transition:opacity 500ms;display:none;'><div id='owInviteMessage' style='padding:0;margin:0 0 20px 0;font-size:16px;'>Got a spare two and a half minutes to help us improve the docs?</div><a id='owInviteOk' href='https://Grafana.optimalworkshop.com/questions/grafana-docs?tag=docs&utm_medium=intercept' onclick='this.parentNode.style.display="none";' target='_blank' style='color:#F7FAFF;font-size:16px;font-weight:bold;text-decoration:underline;'>Yes, I&#x27;ll help</a><a id='owInviteCancel' href='javascript:void(0)' onclick='this.parentNode.style.display="none";' style='color:#F7F8FA;font-size:14px;text-decoration:underline;float:right;'>Close</a></div><script>var owOnload=function(){if(-1==document.cookie.indexOf('ow-intercept-quiz-4ior230e')){var o=new XMLHttpRequest;o.onloadend=function(){try{var o=document.getElementById('owInviteSnippet');var date=new Date();date.setMonth(date.getMonth()+1);this.response&&JSON.parse(this.response).active===!0&&(document.cookie='ow-intercept-quiz-4ior230e=Done;path=/;expires='+date.toUTCString()+';',setTimeout(function(){o.style.display='block',o.style.opacity=1},2e3))}catch(e){}},o.open('POST','https://app.optimalworkshop.com/survey_status/questions/4ior230e/active'),o.send()}};if(window.addEventListener){window.addEventListener('load',function(){owOnload();});}else if(window.attachEvent){window.attachEvent('onload',function(){owOnload();});}</script><!-- END Optimal Workshop snippet -->
+6 -8
View File
@@ -9,11 +9,9 @@ aliases = ["/docs/grafana/latest/reference/datalinks/"]
Data links allow you to provide more granular context to your links. You can create links that include the series name or even the value under the cursor. For example, if your visualization showed four servers, you could add a data link to one or two of them.
The link itself is accessible in different ways depending on the visualization. For the graph you need to click on a data point or line, for a panel like
The link itself is accessible in different ways depending on the visualization. For the Graph you need to click on a data point or line, for a panel like
Stat, Gauge, or Bar Gauge you can click anywhere on the visualization to open the context menu.
> **Note:** For stat, gauge, bar gauge, and table visualizations, you add and edit data links on the Field tab. For the graph visualization, you add and edit data links on the Panel tab.
You can use variables in data links to send people to a detailed dashboard with preserved data filters. For example, you could use variables to specify a time range, series, and variable selection. For more information, refer to [Data link variables]({{< relref "data-link-variables.md" >}}).
## Typeahead suggestions
@@ -25,7 +23,7 @@ When creating or updating a data link, press Cmd+Space or Ctrl+Space on your key
## Add a data link
1. Hover your cursor over the panel that you want to add a link to and then press `e`. Or click the dropdown arrow next to the panel title and then click **Edit**.
1. On the Field tab, scroll down to the Data links section. (Panel tab for graph visualizations.)
1. On the Field tab, scroll down to the Data links section.
1. Expand Data links and then click **Add link**.
1. Enter a **Title**. **Title** is a human-readable label for the link that will be displayed in the UI.
1. Enter the **URL** you want to link to.
@@ -38,14 +36,14 @@ When creating or updating a data link, press Cmd+Space or Ctrl+Space on your key
## Update a data link
1. On the Field tab, find the link that you want to make changes to. (Panel tab for graph visualizations.)
1. Click the Edit (pencil) icon to open the Edit link window.
1. On the Field tab, find the link that you want to make changes to.
1. Click the Edit (pencil) icon to open the Edit link window.
1. Make any necessary changes.
1. Click **Save** to save changes and close the window.
1. Click **Save** in the upper right to save your changes to the dashboard.
## Delete a data link
1. On the Field tab, find the link that you want to delete. (Panel tab for graph visualizations.)
1. Click the **X** icon next to the link you want to delete.
1. On the Field tab, find the link that you want to delete.
1. Click the **X** icon next to the link you want to delete.
1. Click **Save** in the upper right to save your changes to the dashboard.
+4 -4
View File
@@ -438,8 +438,8 @@
link: /plugins/
- name: Install plugins
link: /plugins/installation/
- name: Plugin signature verification
link: /plugins/plugin-signature-verification/
- name: Plugin signatures
link: /plugins/plugin-signatures/
- name: HTTP APIs
link: /http_api/
children:
@@ -537,8 +537,8 @@
name: Plugin protocol
- link: /developers/plugins/backend/grafana-plugin-sdk-for-go/
name: Grafana plugin SDK for Go
- name: Share a plugin
link: /developers/plugins/share-a-plugin/
- name: Package a plugin
link: /developers/plugins/package-a-plugin/
- name: Error handling
link: /developers/plugins/error-handling/
- name: Plugin migration guide
+1
View File
@@ -21,6 +21,7 @@ Among other places, these calculations are used in the Transform tab and the bar
| Count | number of values in a field|
| Delta | cumulative change in value |
| Difference | difference between first and last value of a field|
| Difference percent | percentage change between first and last value of a field |
| Distinct count | number of unique values in a field |
| First (not null) | first, not null value in a field|
| Max | maximum value of a field |
@@ -18,6 +18,8 @@ For more information about applying these options, refer to:
- [Configure all fields]({{< relref "configure-all-fields.md" >}})
- [Configure specific fields]({{< relref "configure-specific-fields.md" >}})
> **Note:** We are constantly working to add and expand options for all visualization, so all options might not be available for all visualizations.
## Decimals
Number of decimals to render value with. Leave empty for Grafana to use the number of decimals provided by the data source.
@@ -16,8 +16,9 @@ This visualization is the most-used in the Grafana ecosystem. It can render as a
Graph visualizations allow you to apply:
- [Data transformations]({{< relref "../transformations/_index.md" >}})
- [Alerts]({{< relref "../../alerting/alerts-overview.md" >}}) - This is the only type of visualization that allows you to set alerts.
- [Data transformations]({{< relref "../transformations/_index.md" >}})
- [Field options and overrides]({{< relref "../field-options/_index.md" >}})
- [Thresholds]({{< relref "../thresholds.md" >}})
## Display options
+2 -2
View File
@@ -44,7 +44,7 @@ Use app plugins when you want to create an custom out-of-the-box monitoring expe
## Learn more
- [Install plugins]({{< relref "./installation.md" >}})
- [Plugin signature verification]({{< relref "./plugin-signature-verification.md" >}})
- [Plugin signatures]({{< relref "./plugin-signatures.md" >}})
- Browse the available [Plugins](https://grafana.com/grafana/plugins)
<!-- BEGIN Optimal Workshop Intercept Snippet --><div id='owInviteSnippet' style='position:fixed;right:20px;bottom:20px;width:280px;padding:20px;margin:0;border-radius:6px;background:#1857B8;color:#F7F8FA;text-align:left;z-index:2200000000;opacity:0;transition:opacity 500ms;-webkit-transition:opacity 500ms;display:none;'><div id='owInviteMessage' style='padding:0;margin:0 0 20px 0;font-size:16px;'>Got a spare two and a half minutes to help us improve the docs?</div><a id='owInviteOk' href='https://Grafana.optimalworkshop.com/questions/grafana-docs?tag=docs&utm_medium=intercept' onclick='this.parentNode.style.display="none";' target='_blank' style='color:#F7FAFF;font-size:16px;font-weight:bold;text-decoration:underline;'>Yes, I&#x27;ll help</a><a id='owInviteCancel' href='javascript:void(0)' onclick='this.parentNode.style.display="none";' style='color:#F7F8FA;font-size:14px;text-decoration:underline;float:right;'>Close</a></div><script>var owOnload=function(){if(-1==document.cookie.indexOf('ow-intercept-quiz-4ior230e')){var o=new XMLHttpRequest;o.onloadend=function(){try{var o=document.getElementById('owInviteSnippet');var date=new Date();date.setMonth(date.getMonth()+1);this.response&&JSON.parse(this.response).active===!0&&(document.cookie='ow-intercept-quiz-4ior230e=Done;path=/;expires='+date.toUTCString()+';',setTimeout(function(){o.style.display='block',o.style.opacity=1},2e3))}catch(e){}},o.open('POST','https://app.optimalworkshop.com/survey_status/questions/4ior230e/active'),o.send()}};if(window.addEventListener){window.addEventListener('load',function(){owOnload();});}else if(window.attachEvent){window.attachEvent('onload',function(){owOnload();});}</script><!-- END Optimal Workshop snippet -->
<!-- BEGIN Optimal Workshop Intercept Snippet --><div id='owInviteSnippet' style='position:fixed;right:20px;bottom:20px;width:280px;padding:20px;margin:0;border-radius:6px;background:#1857B8;color:#F7F8FA;text-align:left;z-index:2200000000;opacity:0;transition:opacity 500ms;-webkit-transition:opacity 500ms;display:none;'><div id='owInviteMessage' style='padding:0;margin:0 0 20px 0;font-size:16px;'>Got a spare two and a half minutes to help us improve the docs?</div><a id='owInviteOk' href='https://Grafana.optimalworkshop.com/questions/grafana-docs?tag=docs&utm_medium=intercept' onclick='this.parentNode.style.display="none";' target='_blank' style='color:#F7FAFF;font-size:16px;font-weight:bold;text-decoration:underline;'>Yes, I&#x27;ll help</a><a id='owInviteCancel' href='javascript:void(0)' onclick='this.parentNode.style.display="none";' style='color:#F7F8FA;font-size:14px;text-decoration:underline;float:right;'>Close</a></div><script>var owOnload=function(){if(-1==document.cookie.indexOf('ow-intercept-quiz-4ior230e')){var o=new XMLHttpRequest;o.onloadend=function(){try{var o=document.getElementById('owInviteSnippet');var date=new Date();date.setMonth(date.getMonth()+1);this.response&&JSON.parse(this.response).active===!0&&(document.cookie='ow-intercept-quiz-4ior230e=Done;path=/;expires='+date.toUTCString()+';',setTimeout(function(){o.style.display='block',o.style.opacity=1},2e3))}catch(e){}},o.open('POST','https://app.optimalworkshop.com/survey_status/questions/4ior230e/active'),o.send()}};if(window.addEventListener){window.addEventListener('load',function(){owOnload();});}else if(window.attachEvent){window.attachEvent('onload',function(){owOnload();});}</script><!-- END Optimal Workshop snippet -->
+8
View File
@@ -25,3 +25,11 @@ Grafana Cloud handles the plugin installation automatically.
Follow the instructions on the Install tab. You can either install the plugin with a Grafana CLI command or by downloading and uncompress a .zip file into the Grafana plugins directory. We recommend using Grafana CLI in most instances. The .zip option is available if your Grafana server does not have access to the internet.
For more information about Grafana CLI plugin commands, refer to [Plugin commands]({{< relref "../administration/cli.md#plugins-commands" >}}).
### Install a packaged plugin
After the user has downloaded the archive containing the plugin assets, they can install it by extracting the archive into their plugin directory.
```
unzip my-plugin-0.2.0.zip -d YOUR_PLUGIN_DIR/my-plugin
```
@@ -1,85 +0,0 @@
+++
title = "Plugin signature verification"
type = "docs"
+++
# Plugin signature verification
Plugin signature verification (signing) is a security measure to make sure plugins haven't been tampered with. Upon loading, Grafana checks to see if a plugin is signed or unsigned when inspecting and verifying its digital signature.
## How it works
For Grafana to verify the digital signature of a plugin, the plugin must include a signed manifest file, _MANIFEST.txt_. The signed manifest file contains two sections:
- **Signed message -** The signed message contains plugin metadata and plugin files with their respective checksums (SHA256).
- **Digital signature -** The digital signature is created by encrypting the signed message using a private key. Grafana has a public key built-in that can be used to verify that the digital signature have been encrypted using expected private key.
### Signed manifest example file
```txt
// MANIFEST.txt
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
{
"plugin": "grafana-test-plugin",
"version": "1.0.0",
"files": {
"LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
"README.md": "5bfefcdce6eafce3388d1fb200f3b10954cfeac6c7a45fd7dec42687e01ac75d",
"module.js": "3c07596a6a4796c65ef10ba2bc0805e7f3bc9e4e8fc9970a1307b97e29db1c0a",
"module.js.LICENSE.txt": "fdbc28c10f3d21976b4bc16464ad7c630538c0c3101347b5fd44af9066f7022b",
"module.js.map": "c3ac1e8aa98d83c54fd13e43b65e1cf4182a924d2eb23a2f1a6fe40b7785a1bb",
"plugin.json": "cf26a3afb7c10cd9ae40b5296d04172b5dac927d69a51082e6d085b34341ccc3"
},
"time": 1589558058070,
"keyId": "7e4d0c6a708866e7"
}
-----BEGIN PGP SIGNATURE-----
Version: OpenPGP.js v4.10.1
Comment: https://openpgpjs.org
wqAEARMKAAYFAl6+uyoACgkQfk0ManCIZuc0+QIHdWC0dp7GRRFu3Hgk9tnl
FJnPwM6Y2tTdq7AkpVTTAb3RTFadA8dRmLfajxgHxmDf5yUv9M2M6sa1eTSG
8kJtOlwCB096dXOKsH1IOGQMCY+/xM2081FqbMTvWgN81xrxMoxftQn8z6VC
2nA2Rmt1VStppFVCCUXaq6Y4sFGHQF/yq5oi
=vqUQ
-----END PGP SIGNATURE-----
```
## Plugin signatures
When Grafana starts, it discovers plugins to load. For each discovered plugin it verifies the authenticity of it, and then decides whether to load it or not based on the state of the plugin signature:
| Plugin signature | Description |
| ---------------- | ----------- |
| internal | Core plugin built into Grafana. |
| invalid | Digital signature of _MANIFEST.txt_ file is not valid. |
| modified | Manifest plugin id or version have been changed or files checksums doesn't match. |
| unsigned | Plugin don't have a _MANIFEST.txt_ file. |
| valid | If any of the above descriptions is false. |
The plugin signature state can be inspected for each plugin in the plugins listing page (Configuration -> Plugins).
## Backend plugins
If a [backend plugin]({{< relref "../developers/plugins/backend/_index.md" >}}) is not signed, then Grafana will not load or start it. If you try to load a backend plugin with an invalid signature, then Grafana writes an error message to the server log:
```bash
EROR[06-01|16:45:59] Failed to load plugin error=plugin <plugin id> is unsigned
```
> **Note:** All Grafana Labs authored backend plugins, including Enterprise plugins, are signed.
## Allow unsigned plugins
While you can allow unsigned plugins using a configuration setting, we strongly advise you not to. For more information on how to allow unsigned backend plugin, refer to [Configuration]({{< relref "../administration/configuration.md#allow-loading-unsigned-plugins" >}}). Allowing unsigned plugins will not skip verifying the authenticity of a plugin if plugin has a _MANIFEST.txt_ file.
If you run an unsigned backend plugin, then Grafana writes a warning message to the server log:
```bash
WARN[06-01|16:45:59] Running an unsigned backend plugin pluginID=<plugin id>
```
If you're developing plugins and run Grafana from source, the development mode is enabled by default and also allow you to run unsigned backend plugins.
+53
View File
@@ -0,0 +1,53 @@
+++
title = "Plugin signatures"
type = "docs"
aliases = ["/docs/plugins/plugin-signature-verification"]
+++
# Plugin signatures
Plugin signature verification (signing) is a security measure to make sure plugins haven't been tampered with. Upon loading, Grafana checks to see if a plugin is signed or unsigned when inspecting and verifying its digital signature.
At startup, Grafana verifies the signatures of every plugin in the plugin directory. You can see the result of this verification for each plugin by navigating to **Configuration** -> **Plugins**.
> **Note:** If you're a plugin developer and want to know how to sign your plugin, refer to [Sign a plugin]({{< relref "../developers/plugins/sign-a-plugin.md" >}}).
| Signature status | Description |
| ---------------- | ----------- |
| Core | Core plugin built into Grafana. |
| Invalid signature | The plugin has a invalid signature. |
| Modified signature | The plugin has changed since it was signed. This may indicate malicious intent. |
| Unsigned | The plugin is not signed. |
| Signed | The plugin signature was successfully verified. |
## Plugin signature levels
All plugins is signed under a _signature level_. The signature level determines how the plugin can be distributed.
|**Plugin Level**|**Description**|
|---|---|
|Private|<p>Private plugins are for use on your own Grafana. They may not be distributed to the Grafana community, and are not published in the Grafana catalog.</p>|
|Community|<p>Community plugins have dependent technologies that are open source and not for profit.</p><p>Community plugins are published in the official Grafana catalog, and are available to the Grafana community.</p>|
|Commercial|<p>Commercial plugins have dependent technologies that are closed source or commercially backed.</p><p>Commercial Plugins are published on the official Grafana catalog, and are available to the Grafana community.</p>|
## Backend plugins
If a [backend plugin]({{< relref "../developers/plugins/backend/_index.md" >}}) is unsigned, then Grafana won't load or start it. If you try to load a backend plugin with an missing or invalid signature, then Grafana writes an error message to the server log:
```bash
EROR[06-01|16:45:59] Failed to load plugin error=plugin <plugin id> is unsigned
```
> **Note:** All Grafana Labs authored backend plugins, including Enterprise plugins, are signed.
## Allow unsigned plugins
We strongly recommend that you don't run unsigned plugins in your Grafana installation. If you're aware of the risks and you still want to load an unsigned plugin, refer to [Configuration]({{< relref "../administration/configuration.md#allow-loading-unsigned-plugins" >}}).
If you've allowed loading of an unsigned backend plugin, then Grafana writes a warning message to the server log:
```bash
WARN[06-01|16:45:59] Running an unsigned backend plugin pluginID=<plugin id>
```
> **Note:** If you're developing a plugin, then you can enable development mode to allow all unsigned plugins.
+14 -1
View File
@@ -10,7 +10,20 @@ draft = "true"
# Share a panel
Click a panel title to open the panel menu, then click share in the panel menu to open the Share Panel dialog. Here you have access to a link that will take you to exactly this panel with the current time range and selected template variables. Below are ways to share a panel.
To share a panel
1. Click a panel title to open the panel menu.
1. Click share in the panel menu to open the Share Panel dialog.
Here you have access to a link that will take you to exactly this panel with the current time range and selected template variables. Here are some of the ways you can share a panel.
## Publish snapshots
You can publish snapshots to your local instance or to [snapshot.raintank.io](http://snapshot.raintank.io). The latter is a free service provided by [Raintank](http://raintank.io), that allows you to publish dashboard snapshots to an external Grafana instance.
Anyone with the link can view it. You can optionally set an expiration time if you want the snapshot to be removed after a certain time period.
{{< docs-imagebox img="/img/docs/panels/Share_a_panel.png" max-width="700px" >}}
## Direct Link Rendered Image
@@ -79,4 +79,35 @@ demo.robustperception.io:9090
demo.robustperception.io:9093
demo.robustperception.io:9100
```
## Filter and modify using named text and value capture groups
Using named capture groups, you can capture separate 'text' and 'value' parts from the options returned by the variable query. This allows the variable drop-down list to contain a friendly name for each value that can be selected.
For example, when querying the `node_hwmon_chip_names` Prometheus metric, the `chip_name` is a lot friendlier that the `chip` value. So the following variable query result:
```text
node_hwmon_chip_names{chip="0000:d7:00_0_0000:d8:00_0",chip_name="enp216s0f0np0"} 1
node_hwmon_chip_names{chip="0000:d7:00_0_0000:d8:00_1",chip_name="enp216s0f0np1"} 1
node_hwmon_chip_names{chip="0000:d7:00_0_0000:d8:00_2",chip_name="enp216s0f0np2"} 1
node_hwmon_chip_names{chip="0000:d7:00_0_0000:d8:00_3",chip_name="enp216s0f0np3"} 1
```
Passed through the following Regex:
```regex
/chip_name="(?<text>[^"]+)|chip="(?<value>[^"]+)/g
```
Would produce the following drop-down list:
```text
Display Name Value
------------ -------------------------
enp216s0f0np0 0000:d7:00_0_0000:d8:00_0
enp216s0f0np1 0000:d7:00_0_0000:d8:00_1
enp216s0f0np2 0000:d7:00_0_0000:d8:00_2
enp216s0f0np3 0000:d7:00_0_0000:d8:00_3
```
**Note:** Only `text` and `value` capture group names are supported.
@@ -70,6 +70,7 @@ This variable is the ID of the current organization.
`${__user.id}` is the ID of the current user.
`${__user.login}` is the login handle of the current user.
`${__user.email}` is the email for the current user.
## $__range
+1 -2
View File
@@ -61,7 +61,7 @@ You can also specify mappings between LDAP group memberships and Grafana Organiz
### Basic Auth Support
You can now authenticate against the Grafana API utilizing a simple username and password with basic HTTP authentication.
> **Note:** This can be useful for provisioning and config management systems that need
> **Note:** This can be useful for provisioning and configuring management systems that need
> to utilize the API without having to create an API key.
@@ -129,4 +129,3 @@ string values.
### Changelog
For a detailed list and link to github issues for everything included in the 2.1 release please
view the [CHANGELOG.md](https://github.com/grafana/grafana/blob/master/CHANGELOG.md) file.
+1 -1
View File
@@ -15,7 +15,7 @@ weight = 3
- **Graph**: Support for shared tooltip on all graphs as you hover over one graph. [#1578](https://github.com/grafana/grafana/pull/1578), [#6274](https://github.com/grafana/grafana/pull/6274)
- **Victorops**: Add VictorOps notification integration [#6411](https://github.com/grafana/grafana/issues/6411), thx [@ichekrygin](https://github.com/ichekrygin)
- **Opsgenie**: Add OpsGenie notification integratiion [#6687](https://github.com/grafana/grafana/issues/6687), thx [@kylemcc](https://github.com/kylemcc)
- **Cloudwatch**: Make it possible to specify access and secret key on the data source config page [#6697](https://github.com/grafana/grafana/issues/6697)
- **Cloudwatch**: Make it possible to specify access and secret key on the data source configuration page [#6697](https://github.com/grafana/grafana/issues/6697)
- **Elasticsearch**: Added support for Elasticsearch 5.x [#5740](https://github.com/grafana/grafana/issues/5740), thx [@lpic10](https://github.com/lpic10)
- **Panel**: Added help text for panels. [#4079](https://github.com/grafana/grafana/issues/4079), thx [@utkarshcmu](https://github.com/utkarshcmu)
- [Full changelog](https://github.com/grafana/grafana/blob/master/CHANGELOG.md)
+1 -1
View File
@@ -38,7 +38,7 @@ on but will be not ready for v4 release.
{{< imgbox max-width="40%" img="/img/docs/v4/alerting_conditions.png" caption="Alerting Conditions" >}}
The rule config allows you to specify a name, how often the rule should be evaluated and a series
The rule configuration allows you to specify a name, how often the rule should be evaluated and a series
of conditions that all need to be true for the alert to fire.
Currently the only condition type that exists is a `Query` condition that allows you to
+1 -1
View File
@@ -29,7 +29,7 @@ Grafana v5.3 brings new features, many enhancements and bug fixes. This article
Grafana v5.3 ships with built-in support for [Google Stackdriver](https://cloud.google.com/stackdriver/) and enables you to visualize your Stackdriver metrics in Grafana.
Getting started with the plugin is easy. Simply create a GCE Service account that has access to the Stackdriver API scope, download the Service Account key file from Google and upload it on the Stackdriver data source config page in Grafana and you should have a secure server-to-server authentication setup. Like other core plugins, Stackdriver has built-in support for alerting. It also comes with support for heatmaps and basic variables.
Getting started with the plugin is easy. Simply create a GCE Service account that has access to the Stackdriver API scope, download the Service Account key file from Google and upload it on the Stackdriver data source configuration page in Grafana and you should have a secure server-to-server authentication setup. Like other core plugins, Stackdriver has built-in support for alerting. It also comes with support for heatmaps and basic variables.
If you're already accustomed to the Stackdriver Metrics Explorer UI, you'll notice that there are a lot of similarities to the query editor in Grafana. It is possible to add filters using wildcards and regular expressions. You can do Group By, Primary Aggregation and Alignment.
+2 -2
View File
@@ -27,13 +27,13 @@ Support for fetching a list of label names was released in Prometheus v2.6.0 so
### Permissions: Editors can own dashboards, folders and teams they create
When the dashboard folders feature and permissions system was released in Grafana 5.0, users with the editor role were not allowed to administrate dashboards, folders or teams. In the 6.1 release, we have added a config option that can change the default permissions so that editors are admins for any Dashboard, Folder or Team they create.
When the dashboard folders feature and permissions system was released in Grafana 5.0, users with the editor role were not allowed to administrate dashboards, folders or teams. In the 6.1 release, we have added a configuration option that can change the default permissions so that editors are admins for any Dashboard, Folder or Team they create.
This feature also adds a new Team permission that can be assigned to any user with the editor or viewer role and enables that user to add other users to the Team.
We believe that this is more in line with the Grafana philosophy, as it will allow teams to be more self-organizing. This option will be made permanent if it gets positive feedback from the community so let us know what you think in the [issue on GitHub](https://github.com/grafana/grafana/issues/15590).
To turn this feature on add the following [config option](/administration/configuration/#editors-can-admin) to your Grafana ini file in the `users` section and then restart the Grafana server:
To turn this feature on add the following [configuration option](/administration/configuration/#editors-can-admin) to your Grafana ini file in the `users` section and then restart the Grafana server:
```ini
[users]
@@ -22,6 +22,7 @@ The main highlights are:
- [**Table improvements and new image cell mode**]({{< relref "#table-improvements-and-new-image-cell-mode" >}})
- [**New color scheme option**]({{< relref "#new-color-scheme-option" >}})
- [**SigV4 Authentication for Amazon Elasticsearch Service**]({{< relref "#sigv4-authentication-for-aws-users" >}})
- [**CSV exports for Excel**]({{< relref "#csv-exports-for-excel" >}})
## Table improvements and new image cell mode
@@ -59,6 +60,11 @@ As this new option is a standard field option it works in every panel. Here is a
{{< figure src="/img/docs/v73/bar_gauge_gradient_color_scheme.png" max-width="900px" caption="bar gauge color scheme" >}}
## CSV exports for Excel
In v7.0, we introduced a new table panel and inspect mode with Download CSV enabled. However, CSV export to Excel was removed. Due to a large number of inquiries and requests, this [community contribution from tomdaly](https://github.com/grafana/grafana/pull/27284) brought the feature back.
For more information, refer to [Download raw query results as CSV]({{< relref "../panels/inspect-panel/#download-raw-query-results-as-csv" >}}) in the Grafana documentation.
## Google Cloud monitoring out-of-the-box dashboards
@@ -84,6 +90,10 @@ You can now configure your Elasticsearch data source to access your Amazon Elast
For more details, refer to the [Elasticsearch docs]({{<relref "../datasources/elasticsearch/#aws-signature-version-4-authentication">}}).
## Chaining pipeline aggregation in Elasticsearch
Thanks to a contribution from a community member, it's now possible to chain multiple pipeline aggregations together and use the results of one pipeline aggregation as the input of another. This unleashes the full power of Elasticsearch's pipeline aggregations in Grafana, allowing users to perform high order derivatives or use a pipeline aggregation result as a variable for a Bucket Script Aggregation.
## Grafana Enterprise features
These features are included in the Grafana Enterprise edition software.
@@ -19,6 +19,7 @@ e2e.scenario({
'__org.id = 1',
'__user.id = 1',
'__user.login = admin',
'__user.email = admin@localhost',
`Server:raw = A'A"A,BB\\B,CCC`,
`Server:regex = (A'A"A|BB\\\\B|CCC)`,
`Server:lucene = ("A'A\\"A" OR "BB\\\\B" OR "CCC")`,
@@ -38,7 +39,7 @@ e2e.scenario({
e2e()
.get('.markdown-html li')
.should('have.length', 22)
.should('have.length', 23)
.each(element => {
items.push(element.text());
})
+5 -5
View File
@@ -15,11 +15,11 @@ require (
cloud.google.com/go/storage v1.12.0
github.com/BurntSushi/toml v0.3.1
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f
github.com/aws/aws-sdk-go v1.33.12
github.com/aws/aws-sdk-go v1.35.5
github.com/beevik/etree v1.1.0
github.com/benbjohnson/clock v0.0.0-20161215174838-7dc76406b6d3
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b
github.com/centrifugal/centrifuge v0.11.0
github.com/centrifugal/centrifuge v0.13.0
github.com/crewjam/saml v0.4.1
github.com/davecgh/go-spew v1.1.1
github.com/deepmap/oapi-codegen v1.3.11 // indirect
@@ -33,16 +33,16 @@ require (
github.com/gchaincl/sqlhooks v1.3.0
github.com/go-macaron/binding v0.0.0-20190806013118-0b4f37bab25b
github.com/go-macaron/gzip v0.0.0-20160222043647-cad1c6580a07
github.com/go-macaron/session v0.0.0-20190805070824-1a3cdc6f5659
github.com/go-sql-driver/mysql v1.5.0
github.com/go-stack/stack v1.8.0
github.com/gobwas/glob v0.2.3
github.com/golang/mock v1.4.4
github.com/golang/protobuf v1.4.3
github.com/google/go-cmp v0.5.2
github.com/google/uuid v1.1.2
github.com/gosimple/slug v1.4.2
github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4
github.com/grafana/grafana-plugin-sdk-go v0.78.0
github.com/grafana/grafana-plugin-sdk-go v0.79.0
github.com/grafana/loki v1.6.0
github.com/grpc-ecosystem/go-grpc-middleware v1.2.1
github.com/hashicorp/go-hclog v0.12.2
@@ -50,7 +50,7 @@ require (
github.com/hashicorp/go-version v1.2.0
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec
github.com/influxdata/influxdb-client-go/v2 v2.0.1
github.com/jmespath/go-jmespath v0.3.0
github.com/jmespath/go-jmespath v0.4.0
github.com/jonboulle/clockwork v0.2.1 // indirect
github.com/jung-kurt/gofpdf v1.10.1
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect
+10 -50
View File
@@ -16,9 +16,7 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.66.0 h1:DZeAkuQGQqnm9Xv36SbMJEU8aFBz4wL04UpMWPWwjzg=
cloud.google.com/go v0.66.0/go.mod h1:dgqGAjKCDxyhGTtC9dAREQGUJpkceNm1yt590Qno0Ko=
cloud.google.com/go v0.70.0 h1:ujhG1RejZYi+HYfJNlgBh3j/bVKD8DewM7AkJ5UPyBc=
cloud.google.com/go v0.70.0/go.mod h1:/UTKYRQTWjVnSe7nGvoSzxEFUELzSI/yAYd0JQT6cRo=
@@ -40,9 +38,7 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy
cloud.google.com/go/storage v1.3.0/go.mod h1:9IAwXhoyBJ7z9LcAwkj0/7NnPzYaPeZxxVp3zm+5IqA=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0 h1:86K1Gel7BQ9/WmNWn7dTKMvTLFzwtBe5FNqYbi9X35g=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.12.0 h1:4y3gHptW1EHVtcPAVE0eBBlFuGqEejTTG3KdIE0lUX4=
cloud.google.com/go/storage v1.12.0/go.mod h1:fFLk2dp2oAhDz8QFKwqrjdJvxSp/W2g7nillojlL5Ho=
@@ -153,8 +149,9 @@ github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.33.5/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.33.12 h1:eydMoSwfrSTD9PWKUJOiDL7+/UwDW8AjInUGVE5Llh4=
github.com/aws/aws-sdk-go v1.33.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.35.5 h1:doSEOxC0UkirPcle20Rc+1kAhJ4Ip+GSEeZ3nKl7Qlk=
github.com/aws/aws-sdk-go v1.35.5/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
@@ -175,7 +172,6 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dR
github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
@@ -186,8 +182,8 @@ github.com/cenkalti/backoff v1.0.0/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQ
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/centrifugal/centrifuge v0.11.0 h1:aaG74PBO+rBmz3hE9W8bCGz8kofiam1LNE4EAVtiMaA=
github.com/centrifugal/centrifuge v0.11.0/go.mod h1:jdFw/2dBFpME3OTisc5FVAC1ilqodnyMnQFjem0k0yg=
github.com/centrifugal/centrifuge v0.13.0 h1:nfcpaa/TXoA56ba/xXs3rc2Vrj/UZTFS1iTQ9q2MSZE=
github.com/centrifugal/centrifuge v0.13.0/go.mod h1:jdFw/2dBFpME3OTisc5FVAC1ilqodnyMnQFjem0k0yg=
github.com/centrifugal/protocol v0.3.4 h1:9q22iSp4CQOdQahfopvfmWWxDbj1Lo7ERG2j56mAxkE=
github.com/centrifugal/protocol v0.3.4/go.mod h1:2YbBCaDwQHl37ErRdMrKSj18X2yVvpkQYtSX6aVbe5A=
github.com/cespare/xxhash v0.0.0-20181017004759-096ff4a8a059/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
@@ -227,16 +223,12 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cortexproject/cortex v0.6.1-0.20200228110116-92ab6cbe0995/go.mod h1:3Xa3DjJxtpXqxcMGdk850lcIRb81M0fyY1MQ6udY134=
github.com/cortexproject/cortex v1.2.1-0.20200803161316-7014ff11ed70/go.mod h1:PVPxNLrxKH+yc8asaJOxuz7TiRmMizFfnSMOnRzM6oM=
github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7/go.mod h1:mby/05p8HE5yHEAKiIH/555NoblMs7PtW6NrYshDruc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/crewjam/httperr v0.0.0-20190612203328-a946449404da/go.mod h1:+rmNIXRvYMqLQeR4DHyTvs6y0MEMymTz4vyFpFkKTPs=
github.com/crewjam/saml v0.4.1 h1:ZNSRJvdbypQDY2uApMngeIHNcxS6UCRAgiw3S+pmgRU=
github.com/crewjam/saml v0.4.1/go.mod h1:vHcshzXm2WkPOV1dcToZa99cCB1h3nPiKLtLYK+erBE=
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg=
@@ -356,8 +348,6 @@ github.com/go-macaron/gzip v0.0.0-20160222043647-cad1c6580a07 h1:YSIA98PevNf1NtC
github.com/go-macaron/gzip v0.0.0-20160222043647-cad1c6580a07/go.mod h1://cJFfDp/70L0oTNAMB+M8Jd0rpuIx/55iARuJ6StwE=
github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191 h1:NjHlg70DuOkcAMqgt0+XA+NHwtu66MkTVVgR4fFWbcI=
github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191/go.mod h1:VFI2o2q9kYsC4o7VP1HrEVosiZZTd+MVT3YZx4gqvJw=
github.com/go-macaron/session v0.0.0-20190805070824-1a3cdc6f5659 h1:YXDFNK98PgKeBd+xM2Babdd6gyABG8H+SSAh5+hr0os=
github.com/go-macaron/session v0.0.0-20190805070824-1a3cdc6f5659/go.mod h1:tLd0QEudXocQckwcpCq5pCuTCuYc24I0bRJDuRe9OuQ=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/analysis v0.17.2/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
@@ -500,7 +490,6 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
@@ -517,7 +506,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
@@ -539,7 +527,6 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
@@ -567,7 +554,6 @@ github.com/google/pprof v0.0.0-20201009210932-67992a1a5a35/go.mod h1:kpwsk12EmLe
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -604,8 +590,8 @@ github.com/gosimple/slug v1.4.2 h1:jDmprx3q/9Lfk4FkGZtvzDQ9Cj9eAmsjzeQGp24PeiQ=
github.com/gosimple/slug v1.4.2/go.mod h1:ER78kgg1Mv0NQGlXiDe57DpCyfbNywXXZ9mIorhxAf0=
github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4 h1:SPdxCL9BChFTlyi0Khv64vdCW4TMna8+sxL7+Chx+Ag=
github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4/go.mod h1:nc0XxBzjeGcrMltCDw269LoWF9S8ibhgxolCdA1R8To=
github.com/grafana/grafana-plugin-sdk-go v0.78.0 h1:w43X+b36goTvis4TAW5PzhkGuSJokgm3KKYPOYiENAc=
github.com/grafana/grafana-plugin-sdk-go v0.78.0/go.mod h1:NvxLzGkVhnoBKwzkst6CFfpMFKwAdIUZ1q8ssuLeF60=
github.com/grafana/grafana-plugin-sdk-go v0.79.0 h1:7NVEIMlF8G9H7XUdLX9jH/g01FllE1GEBcFvzXZD+Kw=
github.com/grafana/grafana-plugin-sdk-go v0.79.0/go.mod h1:NvxLzGkVhnoBKwzkst6CFfpMFKwAdIUZ1q8ssuLeF60=
github.com/grafana/loki v1.6.0 h1:vHuFgfhW1iRMCm7/LgnZi02Ifvqj389vWhyfNJlHQTQ=
github.com/grafana/loki v1.6.0/go.mod h1:X+GvtCzAf2ok/xRLLvGB8kuWP1R+75nXnvjCEnenP0s=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
@@ -716,11 +702,13 @@ github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZ
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/jonboulle/clockwork v0.2.1 h1:S/EaQvW6FpWMYAvYvY+OBDvpaM+izu0oiwo5y0MH7U0=
@@ -801,8 +789,6 @@ github.com/linkedin/goavro/v2 v2.9.7 h1:Vd++Rb/RKcmNJjM0HP/JJFMEWa21eUBVKPYlKehO
github.com/linkedin/goavro/v2 v2.9.7/go.mod h1:UgQUb2N/pmueQYH9bfqFioWxzYCZXSfF8Jw03O5sjqA=
github.com/lovoo/gcloud-opentracing v0.3.0/go.mod h1:ZFqk2y38kMDDikZPAK7ynTTGuyt17nSPdS3K5e+ZTBY=
github.com/lufia/iostat v1.1.0/go.mod h1:rEPNA0xXgjHQjuI5Cy05sLlS2oRcSlWHRLrvh/AQ+Pg=
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ=
github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magefile/mage v1.9.0 h1:t3AU2wNwehMCW97vuqQLtw6puppWXHO+O2MHo5a50XE=
github.com/magefile/mage v1.9.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
@@ -1052,7 +1038,6 @@ github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7 h1:J4AOUcOh/t1XbQcJfkEqhzgvMJ2tDxdCVvmHxW5QXao=
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7/go.mod h1:Oz4y6ImuOQZxynhbSXk7btjEfNBtGlj2dcaOvXl2FSM=
github.com/russellhaering/goxmldsig v0.0.0-20200902171629-2e1fbc2c5593 h1:wkyiSzH81tsd3tSoznvnXMIJo0cpHjbFuJhs/E9t/B8=
github.com/russellhaering/goxmldsig v0.0.0-20200902171629-2e1fbc2c5593/go.mod h1:QK8GhXPB3+AfuCrfo0oRISa9NfzeCpWmxeGnqEpDF9o=
@@ -1085,10 +1070,6 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
github.com/shurcooL/vfsgen v0.0.0-20180825020608-02ddb050ef6b/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
github.com/shurcooL/vfsgen v0.0.0-20200627165143-92b8a710ab6c/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0tzqLRu6TS7Id0wMo2N5QzJoKedVeovOpHjnykSzY=
github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg=
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
github.com/siebenmann/go-kstat v0.0.0-20160321171754-d34789b79745/go.mod h1:G81aIFAMS9ECrwBYR9YxhlPjWgrItd+Kje78O6+uqm8=
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@@ -1130,11 +1111,9 @@ github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
github.com/thanos-io/thanos v0.8.1-0.20200109203923-552ffa4c1a0d/go.mod h1:usT/TxtJQ7DzinTt+G9kinDQmRS5sxwu0unVKZ9vdcw=
@@ -1222,9 +1201,7 @@ go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
@@ -1363,7 +1340,6 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201022231255-08b38378de70 h1:Z6x4N9mAi4oF0TbHweCsH618MO6OI6UFgV0FP5n0wBY=
golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
@@ -1373,7 +1349,6 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 h1:ld7aEMNHoBnnDAX15v1T6z31v8HwR2A9FYOuAhWqkwc=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
@@ -1385,7 +1360,6 @@ golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1464,7 +1438,6 @@ golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd h1:WgqgiQvkiZWz7XLhphjt2GI2GcGCTIZs9jqXMWmH+oc=
golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1552,13 +1525,11 @@ golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200603131246-cc40288be839/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200710042808-f1c4188a97a1/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200725200936-102e7d357031 h1:VtIxiVHWPhnny2ZTi4f9/2diZKqyLaq3FUTuud5+khA=
golang.org/x/tools v0.0.0-20200725200936-102e7d357031/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200828161849-5deb26317202/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858 h1:xLt+iB5ksWcZVxqc+g9K41ZHy+6MKWfXCDsjSThnsPA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20200915173823-2db8f0ff891c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
@@ -1567,7 +1538,6 @@ golang.org/x/tools v0.0.0-20201023150057-2f4fa188d925 h1:iGGR3nU1TUd+WTF17QPTTSh
golang.org/x/tools v0.0.0-20201023150057-2f4fa188d925/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1594,7 +1564,6 @@ google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.26.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0 h1:BaiDisFir8O4IJxvAabCGGkQ6yCJegNQqSVoYUNAnbk=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.31.0/go.mod h1:CL+9IBCa2WWU6gRuBWaKqGWLFFwbEUXkfeMkHLQWYWo=
@@ -1609,7 +1578,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
@@ -1651,16 +1619,13 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY
google.golang.org/genproto v0.0.0-20200603110839-e855014d5736/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200710124503-20a17af7bd0e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200724131911-43cab4749ae7 h1:AWgNCmk2V5HZp9AiCDRBExX/b9I0Ey9F8STHDZlhCC4=
google.golang.org/genproto v0.0.0-20200724131911-43cab4749ae7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200831141814-d751682dd103/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d h1:92D1fum1bJLKSdr11OJ+54YeCMCGYIygTA7R/YZxH5M=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200914193844-75d14daec038/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200921151605-7abf4a1a14d5 h1:B9nroC8SSX5GtbVvxPF9tYIVkaCpjhVLOrlAY8ONzm8=
google.golang.org/genproto v0.0.0-20200921151605-7abf4a1a14d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5 h1:YejJbGvoWsTXHab4OKNrzk27Dr7s4lPLnewbHue1+gM=
@@ -1685,12 +1650,9 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0=
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
@@ -1711,7 +1673,6 @@ gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gG
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e/go.mod h1:xsQCaysVCudhrYTfzYWe577fCe7Ceci+6qjO2Rdc0Z4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
@@ -1736,7 +1697,6 @@ gopkg.in/macaron.v1 v1.3.9 h1:Dw+DDRYdXgQyEsPlfAfKz+UA5qVUrH3KPD7JhmZ9MFc=
gopkg.in/macaron.v1 v1.3.9/go.mod h1:uMZCFccv9yr5TipIalVOyAyZQuOH3OkmXvgcWwhJuP4=
gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
gopkg.in/redis.v2 v2.3.2/go.mod h1:4wl9PJ/CqzeHk3LVq1hNLHH8krm3+AXEgut4jVc++LU=
gopkg.in/redis.v5 v5.2.9 h1:MNZYOLPomQzZMfpN3ZtD1uyJ2IDonTTlxYiV/pEApiw=
gopkg.in/redis.v5 v5.2.9/go.mod h1:6gtv0/+A4iM08kdRfocWYB3bLX2tebpNtfKlFT6H4mY=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+2 -2
View File
@@ -1,4 +1,4 @@
{
"stable": "7.2.2",
"testing": "7.3.0-beta2"
"stable": "7.3.1",
"testing": "7.3.1"
}
+9 -12
View File
@@ -73,8 +73,8 @@
"@babel/preset-typescript": "7.8.3",
"@emotion/core": "10.0.27",
"@grafana/api-documenter": "0.9.3",
"@grafana/eslint-config": "2.0.4",
"@grafana/api-extractor": "7.10.1",
"@grafana/eslint-config": "2.0.5",
"@rtsao/plugin-proposal-class-properties": "7.0.1-patch.1",
"@testing-library/jest-dom": "^5.11.3",
"@testing-library/react": "^10.4.8",
@@ -94,7 +94,6 @@
"@types/jquery": "3.3.38",
"@types/lodash": "4.14.149",
"@types/lru-cache": "^5.1.0",
"@types/marked": "1.1.0",
"@types/moment-timezone": "0.5.13",
"@types/mousetrap": "1.6.3",
"@types/node": "13.7.0",
@@ -149,7 +148,6 @@
"grunt-angular-templates": "1.1.0",
"grunt-cli": "1.3.2",
"grunt-contrib-clean": "2.0.0",
"grunt-contrib-compress": "1.6.0",
"grunt-contrib-copy": "1.0.0",
"grunt-exec": "3.0.0",
"grunt-newer": "1.3.0",
@@ -175,7 +173,6 @@
"monaco-editor-webpack-plugin": "1.9.0",
"mutationobserver-shim": "0.3.3",
"ngtemplate-loader": "2.0.1",
"node-sass": "4.13.1",
"optimize-css-assets-webpack-plugin": "5.0.3",
"postcss-browser-reporter": "0.6.0",
"postcss-loader": "3.0.0",
@@ -187,6 +184,7 @@
"regexp-replace-loader": "1.0.1",
"rimraf": "3.0.1",
"rxjs-spy": "^7.5.1",
"sass": "1.27.0",
"sass-lint": "1.12.1",
"sass-loader": "8.0.2",
"sinon": "8.1.1",
@@ -220,11 +218,10 @@
"@types/uuid": "8.3.0",
"@welldone-software/why-did-you-render": "4.0.6",
"abortcontroller-polyfill": "1.4.0",
"angular": "1.6.9",
"angular": "1.8.2",
"angular-bindonce": "0.3.1",
"angular-native-dragdrop": "1.2.2",
"angular-route": "1.6.6",
"angular-sanitize": "1.6.6",
"angular-route": "1.8.2",
"angular-sanitize": "1.8.2",
"antlr4": "^4.8.0",
"baron": "3.0.3",
"brace": "0.11.1",
@@ -248,7 +245,6 @@
"jsurl": "^0.1.5",
"lodash": "4.17.19",
"lru-cache": "^5.1.1",
"marked": "1.1.1",
"md5": "^2.2.1",
"memoize-one": "5.1.1",
"moment": "2.24.0",
@@ -281,7 +277,7 @@
"regenerator-runtime": "0.13.3",
"reselect": "4.0.0",
"rst2html": "github:thoward/rst2html#990cb89",
"rxjs": "6.6.2",
"rxjs": "6.6.3",
"search-query-parser": "1.5.4",
"slate": "0.47.8",
"slate-plain-serializer": "0.7.10",
@@ -291,7 +287,8 @@
"tinycolor2": "1.4.1",
"tti-polyfill": "0.2.2",
"uuid": "8.3.0",
"whatwg-fetch": "3.1.0"
"whatwg-fetch": "3.1.0",
"visjs-network": "4.25.0"
},
"resolutions": {
"caniuse-db": "1.0.30000772"
@@ -310,7 +307,7 @@
"puppeteer": "node_modules/puppeteer-core"
},
"engines": {
"node": ">=12 <13"
"node": ">= 12"
},
"volta": {
"node": "12.19.0"
+4 -1
View File
@@ -25,8 +25,10 @@
"@braintree/sanitize-url": "4.0.0",
"@types/d3-interpolate": "^1.3.1",
"apache-arrow": "0.16.0",
"eventemitter3": "4.0.7",
"lodash": "4.17.19",
"rxjs": "6.6.2",
"rxjs": "6.6.3",
"marked": "1.2.2",
"xss": "1.0.6"
},
"devDependencies": {
@@ -39,6 +41,7 @@
"@types/jquery": "3.3.38",
"@types/lodash": "4.14.123",
"@types/node": "10.14.1",
"@types/marked": "1.1.0",
"@types/papaparse": "5.2.0",
"@types/react": "16.8.16",
"@types/rollup-plugin-visualizer": "2.6.0",
@@ -0,0 +1,173 @@
import { EventBusSrv } from './EventBus';
import { BusEventWithPayload } from './types';
import { eventFactory } from './eventFactory';
interface LoginEventPayload {
logins: number;
}
interface HelloEventPayload {
hellos: number;
}
class LoginEvent extends BusEventWithPayload<LoginEventPayload> {
static type = 'login-event';
}
class HelloEvent extends BusEventWithPayload<HelloEventPayload> {
static type = 'hello-event';
}
type LegacyEventPayload = [string, string];
export const legacyEvent = eventFactory<LegacyEventPayload>('legacy-event');
class AlertSuccessEvent extends BusEventWithPayload<LegacyEventPayload> {
static type = 'legacy-event';
}
describe('EventBus', () => {
it('Can create events', () => {
expect(new LoginEvent({ logins: 1 }).type).toBe('login-event');
});
it('Can subscribe specific event', () => {
const bus = new EventBusSrv();
const events: LoginEvent[] = [];
bus.subscribe(LoginEvent, event => {
events.push(event);
});
bus.publish(new LoginEvent({ logins: 10 }));
bus.publish(new HelloEvent({ hellos: 10 }));
expect(events[0].payload.logins).toBe(10);
expect(events.length).toBe(1);
});
describe('Legacy emitter behavior', () => {
it('Supports legacy events', () => {
const bus = new EventBusSrv();
const events: any = [];
const handler = (event: LegacyEventPayload) => {
events.push(event);
};
bus.on(legacyEvent, handler);
bus.emit(legacyEvent, ['hello', 'hello2']);
bus.off(legacyEvent, handler);
bus.emit(legacyEvent, ['hello', 'hello2']);
expect(events.length).toEqual(1);
expect(events[0]).toEqual(['hello', 'hello2']);
});
it('Interoperability with legacy events', () => {
const bus = new EventBusSrv();
const legacyEvents: any = [];
const newEvents: any = [];
bus.on(legacyEvent, event => {
legacyEvents.push(event);
});
bus.subscribe(AlertSuccessEvent, event => {
newEvents.push(event);
});
bus.emit(legacyEvent, ['legacy', 'params']);
bus.publish(new AlertSuccessEvent(['new', 'event']));
expect(legacyEvents).toEqual([
['legacy', 'params'],
['new', 'event'],
]);
expect(newEvents).toEqual([
{
type: 'legacy-event',
payload: ['legacy', 'params'],
},
{
type: 'legacy-event',
payload: ['new', 'event'],
},
]);
});
it('should notfiy subscribers', () => {
const bus = new EventBusSrv();
let sub1Called = false;
let sub2Called = false;
bus.on(legacyEvent, () => {
sub1Called = true;
});
bus.on(legacyEvent, () => {
sub2Called = true;
});
bus.emit(legacyEvent, null);
expect(sub1Called).toBe(true);
expect(sub2Called).toBe(true);
});
it('when subscribing twice', () => {
const bus = new EventBusSrv();
let sub1Called = 0;
function handler() {
sub1Called += 1;
}
bus.on(legacyEvent, handler);
bus.on(legacyEvent, handler);
bus.emit(legacyEvent, null);
expect(sub1Called).toBe(2);
});
it('should handle errors', () => {
const bus = new EventBusSrv();
let sub1Called = 0;
let sub2Called = 0;
bus.on(legacyEvent, () => {
sub1Called++;
throw { message: 'hello' };
});
bus.on(legacyEvent, () => {
sub2Called++;
});
try {
bus.emit(legacyEvent, null);
} catch (_) {}
try {
bus.emit(legacyEvent, null);
} catch (_) {}
expect(sub1Called).toBe(2);
expect(sub2Called).toBe(0);
});
it('removeAllListeners should unsubscribe to all', () => {
const bus = new EventBusSrv();
const events: LoginEvent[] = [];
bus.subscribe(LoginEvent, event => {
events.push(event);
});
bus.removeAllListeners();
bus.publish(new LoginEvent({ logins: 10 }));
expect(events.length).toBe(0);
});
});
});
@@ -0,0 +1,85 @@
import EventEmitter from 'eventemitter3';
import { Unsubscribable, Observable } from 'rxjs';
import { AppEvent } from './types';
import { EventBus, LegacyEmitter, BusEventHandler, BusEventType, LegacyEventHandler, BusEvent } from './types';
/**
* @alpha
*/
export class EventBusSrv implements EventBus, LegacyEmitter {
private emitter: EventEmitter;
constructor() {
this.emitter = new EventEmitter();
}
publish<T extends BusEvent>(event: T): void {
this.emitter.emit(event.type, event);
}
subscribe<T extends BusEvent>(typeFilter: BusEventType<T>, handler: BusEventHandler<T>): Unsubscribable {
return this.getStream(typeFilter).subscribe({ next: handler });
}
getStream<T extends BusEvent>(eventType: BusEventType<T>): Observable<T> {
return new Observable<T>(observer => {
const handler = (event: T) => {
observer.next(event);
};
this.emitter.on(eventType.type, handler);
return () => {
this.emitter.off(eventType.type, handler);
};
});
}
/**
* Legacy functions
*/
emit<T>(event: AppEvent<T> | string, payload?: T | any): void {
// console.log(`Deprecated emitter function used (emit), use $emit`);
if (typeof event === 'string') {
this.emitter.emit(event, { type: event, payload });
} else {
this.emitter.emit(event.name, { type: event.name, payload });
}
}
on<T>(event: AppEvent<T> | string, handler: LegacyEventHandler<T>, scope?: any) {
// console.log(`Deprecated emitter function used (on), use $on`);
// need this wrapper to make old events compatible with old handlers
handler.wrapper = (emittedEvent: BusEvent) => {
handler(emittedEvent.payload);
};
if (typeof event === 'string') {
this.emitter.on(event, handler.wrapper);
} else {
this.emitter.on(event.name, handler.wrapper);
}
if (scope) {
const unbind = scope.$on('$destroy', () => {
this.off(event, handler);
unbind();
});
}
}
off<T>(event: AppEvent<T> | string, handler: LegacyEventHandler<T>) {
if (typeof event === 'string') {
this.emitter.off(event, handler.wrapper);
return;
}
this.emitter.off(event.name, handler.wrapper);
}
removeAllListeners() {
this.emitter.removeAllListeners();
}
}
@@ -1,9 +1,7 @@
import { AppEvent } from './appEvents';
export type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
export type Subtract<T, K> = Omit<T, keyof K>;
import { AppEvent } from './types';
const typeList: Set<string> = new Set();
export function eventFactory<T = undefined>(name: string): AppEvent<T> {
if (typeList.has(name)) {
throw new Error(`There is already an event defined with type '${name}'`);
@@ -0,0 +1,3 @@
export * from './eventFactory';
export * from './types';
export * from './EventBus';
+115
View File
@@ -0,0 +1,115 @@
import { Unsubscribable, Observable } from 'rxjs';
/**
* @alpha
* internal interface
*/
export interface BusEvent {
readonly type: string;
readonly payload?: any;
}
/**
* @alpha
* Base event type
*/
export abstract class BusEventBase implements BusEvent {
readonly type: string;
readonly payload?: any;
constructor() {
//@ts-ignore
this.type = this.__proto__.constructor.type;
}
}
/**
* @alpha
* Base event type with payload
*/
export abstract class BusEventWithPayload<T> extends BusEventBase {
readonly payload: T;
constructor(payload: T) {
super();
this.payload = payload;
}
}
/*
* Interface for an event type constructor
*/
export interface BusEventType<T extends BusEvent> {
type: string;
new (...args: any[]): T;
}
/**
* @alpha
* Event callback/handler type
*/
export interface BusEventHandler<T extends BusEvent> {
(event: T): void;
}
/**
* @alpha
* Main minimal interface
*/
export interface EventBus {
/**
* Publish single vent
*/
publish<T extends BusEvent>(event: T): void;
/**
* Subscribe to single event
*/
subscribe<T extends BusEvent>(eventType: BusEventType<T>, handler: BusEventHandler<T>): Unsubscribable;
/**
* Get observable of events
*/
getStream<T extends BusEvent>(eventType: BusEventType<T>): Observable<T>;
/**
* Remove all event subscriptions
*/
removeAllListeners(): void;
}
/**
* @public
* @deprecated event type
*/
export interface AppEvent<T> {
readonly name: string;
payload?: T;
}
/** @public */
export interface LegacyEmitter {
/**
* @deprecated use $emit
*/
emit<T>(event: AppEvent<T> | string, payload?: T): void;
/**
* @deprecated use $on
*/
on<T>(event: AppEvent<T> | string, handler: LegacyEventHandler<T>, scope?: any): void;
/**
* @deprecated use $on
*/
off<T>(event: AppEvent<T> | string, handler: (payload?: T | any) => void): void;
}
/** @public */
export interface LegacyEventHandler<T> {
(payload: T): void;
wrapper?: (event: BusEvent) => void;
}
/** @alpha */
export interface EventBusExtended extends EventBus, LegacyEmitter {}
@@ -1,4 +1,4 @@
import { getDisplayProcessor, getRawDisplayProcessor } from './displayProcessor';
import { getDecimalsForValue, getDisplayProcessor, getRawDisplayProcessor } from './displayProcessor';
import { DisplayProcessor, DisplayValue } from '../types/displayValue';
import { MappingType, ValueMapping } from '../types/valueMapping';
import { FieldConfig, FieldType, ThresholdsMode } from '../types';
@@ -329,3 +329,21 @@ describe('getRawDisplayProcessor', () => {
expect(result).toEqual({ text: expected, numeric: null });
});
});
describe('getDecimalsForValue', () => {
it.each`
value | expected
${0} | ${0}
${13.37} | ${0}
${-13.37} | ${0}
${12679.3712345811212} | ${0}
${-12679.3712345811212} | ${0}
${0.3712345} | ${2}
${-0.37123458} | ${2}
${-0.04671994403853774} | ${3}
${0.04671994403853774} | ${3}
`('should return correct suggested decimal count', ({ value, expected }) => {
const result = getDecimalsForValue(value);
expect(result.decimals).toEqual(expected);
});
});
@@ -143,7 +143,7 @@ export function getDecimalsForValue(value: number, decimalOverride?: DecimalCoun
return { decimals: decimalOverride, scaledDecimals: null };
}
let dec = -Math.floor(Math.log(value) / Math.LN10) + 1;
let dec = -Math.floor(Math.log(Math.abs(value)) / Math.LN10) + 1;
const magn = Math.pow(10, -dec);
const norm = value / magn; // norm is between 1.0 and 10.0
let size;
@@ -24,6 +24,7 @@ export const fieldColorModeRegistry = new Registry<FieldColorMode>(() => {
{
id: FieldColorModeId.Thresholds,
name: 'From thresholds',
isByValue: true,
description: 'Derive colors from thresholds',
getCalculator: (_field, theme) => {
return (_value, _percent, threshold) => {
@@ -66,6 +67,13 @@ export const fieldColorModeRegistry = new Registry<FieldColorMode>(() => {
isByValue: true,
colors: ['green', 'yellow', 'red'],
}),
new FieldColorSchemeMode({
id: 'continuous-RdYlGr',
name: 'Red-Yellow-Green',
isContinuous: true,
isByValue: true,
colors: ['red', 'yellow', 'green'],
}),
new FieldColorSchemeMode({
id: 'continuous-BlYlRd',
name: 'Blue-Yellow-Red',
@@ -11,14 +11,14 @@ import { MutableDataFrame, toDataFrame } from '../dataframe';
import {
DataFrame,
Field,
FieldColorModeId,
FieldConfig,
FieldConfigPropertyItem,
FieldConfigSource,
FieldType,
InterpolateFunction,
ThresholdsMode,
FieldColorModeId,
ScopedVars,
ThresholdsMode,
} from '../types';
import { locationUtil, Registry } from '../utils';
import { mockStandardProperties } from '../utils/tests/mockStandardProperties';
@@ -87,6 +87,21 @@ describe('Global MinMax', () => {
expect(minmax.min).toEqual(-20);
expect(minmax.max).toEqual(1234);
});
describe('when value is null', () => {
it('then global min max should be null', () => {
const frame = toDataFrame({
fields: [
{ name: 'Time', type: FieldType.time, values: [1] },
{ name: 'Value', type: FieldType.number, values: [null] },
],
});
const { min, max } = findNumericFieldMinMax([frame]);
expect(min).toBeNull();
expect(max).toBeNull();
});
});
});
describe('applyFieldOverrides', () => {
@@ -55,11 +55,23 @@ export function findNumericFieldMinMax(data: DataFrame[]): GlobalMinMax {
for (const field of frame.fields) {
if (field.type === FieldType.number) {
const stats = reduceField({ field, reducers });
if (stats[ReducerID.min] < min) {
min = stats[ReducerID.min];
const statsMin = stats[ReducerID.min];
const statsMax = stats[ReducerID.max];
if (!statsMin) {
min = statsMin;
}
if (stats[ReducerID.max] > max) {
max = stats[ReducerID.max];
if (!statsMax) {
max = statsMax;
}
if (statsMin && statsMin < min) {
min = statsMin;
}
if (statsMax && statsMax > max) {
max = statsMax;
}
}
}
@@ -294,7 +306,6 @@ const processFieldConfigValue = (
const currentConfig = get(destination, fieldConfigProperty.path);
if (currentConfig === null || currentConfig === undefined) {
const item = context.fieldConfigRegistry.getIfExists(fieldConfigProperty.id);
// console.log(item);
if (!item) {
return;
}
@@ -24,6 +24,12 @@ export const numberOverrideProcessor = (
return parseFloat(value);
};
export interface SliderFieldConfigSettings {
min: number;
max: number;
step?: number;
}
export interface DataLinksFieldConfigSettings {}
export const dataLinksOverrideProcessor = (
@@ -114,8 +120,15 @@ export const booleanOverrideProcessor = (
return value; // !!!! likely not !!!!
};
export interface ColorFieldConfigSettings {
allowUndefined?: boolean;
textWhenUndefined?: string; // Pick Color
disableNamedColors?: boolean;
export interface FieldColorConfigSettings {
/**
* When switching to a visualization that does not support by value coloring then Grafana will
* switch to a by series palette based color mode
*/
byValueSupport?: boolean;
/**
* When switching to a visualization that has this set to true then Grafana will change color mode
* to from thresholds if it was set to a by series palette
*/
preferThresholdsMode?: boolean;
}
+1
View File
@@ -12,4 +12,5 @@ export * from './datetime';
export * from './text';
export * from './valueFormats';
export * from './field';
export * from './events';
export { PanelPlugin } from './panel/PanelPlugin';
@@ -9,11 +9,11 @@ describe('PanelPlugin', () => {
standardFieldConfigEditorRegistry.setInit(() => {
return [
{
id: 'min',
id: FieldConfigProperty.Min,
path: 'min',
},
{
id: 'max',
id: FieldConfigProperty.Max,
path: 'max',
},
] as any;
@@ -210,15 +210,15 @@ describe('PanelPlugin', () => {
expect(panel.fieldConfigRegistry.list()).toHaveLength(2);
});
test('selected standard config', () => {
test('disabling standard config properties', () => {
const panel = new PanelPlugin(() => {
return <div>Panel</div>;
});
panel.useFieldConfig({
standardOptions: [FieldConfigProperty.Min, FieldConfigProperty.Max],
disableStandardOptions: [FieldConfigProperty.Min],
});
expect(panel.fieldConfigRegistry.list()).toHaveLength(2);
expect(panel.fieldConfigRegistry.list()).toHaveLength(1);
});
describe('default values', () => {
@@ -228,10 +228,9 @@ describe('PanelPlugin', () => {
});
panel.useFieldConfig({
standardOptions: [FieldConfigProperty.Max, FieldConfigProperty.Min],
standardOptionsDefaults: {
[FieldConfigProperty.Max]: 20,
[FieldConfigProperty.Min]: 10,
standardOptions: {
[FieldConfigProperty.Max]: { defaultValue: 20 },
[FieldConfigProperty.Min]: { defaultValue: 10 },
},
});
@@ -247,17 +246,16 @@ describe('PanelPlugin', () => {
});
});
it('should ignore defaults that are not specified as available properties', () => {
it('should disable properties independently from the default values settings', () => {
const panel = new PanelPlugin(() => {
return <div>Panel</div>;
});
panel.useFieldConfig({
standardOptions: [FieldConfigProperty.Max],
standardOptionsDefaults: {
[FieldConfigProperty.Max]: 20,
[FieldConfigProperty.Min]: 10,
standardOptions: {
[FieldConfigProperty.Max]: { defaultValue: 20 },
},
disableStandardOptions: [FieldConfigProperty.Min],
});
expect(panel.fieldConfigRegistry.list()).toHaveLength(1);
+48 -31
View File
@@ -15,33 +15,38 @@ import set from 'lodash/set';
import { deprecationWarning } from '../utils';
import { FieldConfigOptionsRegistry, standardFieldConfigEditorRegistry } from '../field';
type StandardOptionConfig = {
defaultValue?: any;
settings?: any;
};
export interface SetFieldConfigOptionsArgs<TFieldConfigOptions = any> {
/**
* Array of standard field config properties
* Configuration object of the standard field config properites
*
* @example
* ```typescript
* {
* standardOptions: [FieldConfigProperty.Min, FieldConfigProperty.Max, FieldConfigProperty.Unit]
* }
* ```
*/
standardOptions?: FieldConfigProperty[];
/**
* Object specifying standard option properties default values
*
* @example
* ```typescript
* {
* standardOptionsDefaults: {
* [FieldConfigProperty.Min]: 20,
* [FieldConfigProperty.Max]: 100
* standardOptions: {
* [FieldConfigProperty.Decimals]: {
* defaultValue: 3
* }
* }
* }
* ```
*/
standardOptionsDefaults?: Partial<Record<FieldConfigProperty, any>>;
standardOptions?: Partial<Record<FieldConfigProperty, StandardOptionConfig>>;
/**
* Array of standard field config properties that should not be available in the panel
* @example
* ```typescript
* {
* disableStandardOptions: [FieldConfigProperty.Min, FieldConfigProperty.Max, FieldConfigProperty.Unit]
* }
* ```
*/
disableStandardOptions?: FieldConfigProperty[];
/**
* Function that allows custom field config properties definition.
@@ -305,13 +310,13 @@ export class PanelPlugin<TOptions = any, TFieldConfigOptions extends object = an
*
* @public
*/
useFieldConfig(config?: SetFieldConfigOptionsArgs<TFieldConfigOptions>) {
useFieldConfig(config: SetFieldConfigOptionsArgs<TFieldConfigOptions> = {}) {
// builder is applied lazily when custom field configs are accessed
this._initConfigRegistry = () => {
const registry = new FieldConfigOptionsRegistry();
// Add custom options
if (config && config.useCustomConfig) {
if (config.useCustomConfig) {
const builder = new FieldConfigEditorBuilder<TFieldConfigOptions>();
config.useCustomConfig(builder);
@@ -326,20 +331,32 @@ export class PanelPlugin<TOptions = any, TFieldConfigOptions extends object = an
}
}
if (config && config.standardOptions) {
for (const standardOption of config.standardOptions) {
const standardEditor = standardFieldConfigEditorRegistry.get(standardOption);
registry.register({
...standardEditor,
defaultValue:
(config.standardOptionsDefaults && config.standardOptionsDefaults[standardOption]) ||
standardEditor.defaultValue,
});
for (let fieldConfigProp of standardFieldConfigEditorRegistry.list()) {
if (config.disableStandardOptions) {
const isDisabled = config.disableStandardOptions.indexOf(fieldConfigProp.id as FieldConfigProperty) > -1;
if (isDisabled) {
continue;
}
}
} else {
for (const fieldConfigProp of standardFieldConfigEditorRegistry.list()) {
registry.register(fieldConfigProp);
if (config.standardOptions) {
const customDefault: any = config.standardOptions[fieldConfigProp.id as FieldConfigProperty]?.defaultValue;
const customSettings: any = config.standardOptions[fieldConfigProp.id as FieldConfigProperty]?.settings;
if (customDefault) {
fieldConfigProp = {
...fieldConfigProp,
defaultValue: customDefault,
};
}
if (customSettings) {
fieldConfigProp = {
...fieldConfigProp,
settings: fieldConfigProp.settings ? { ...fieldConfigProp.settings, ...customSettings } : customSettings,
};
}
}
registry.register(fieldConfigProp);
}
return registry;
@@ -5,4 +5,9 @@ describe('Markdown wrapper', () => {
const str = renderMarkdown(undefined);
expect(str).toBe('');
});
it('should sanitize by default', () => {
const str = renderMarkdown('<script>alert()</script>');
expect(str).toBe('&lt;script&gt;alert()&lt;/script&gt;');
});
});
+23 -14
View File
@@ -1,19 +1,28 @@
import marked, { MarkedOptions } from 'marked';
import marked from 'marked';
import { sanitize } from './sanitize';
const defaultMarkedOptions: MarkedOptions = {
renderer: new marked.Renderer(),
pedantic: false,
gfm: true,
sanitize: true,
smartLists: true,
smartypants: false,
xhtml: false,
};
let hasInitialized = false;
export function setMarkdownOptions(optionsOverride?: MarkedOptions) {
marked.setOptions({ ...defaultMarkedOptions, ...optionsOverride });
export interface RenderMarkdownOptions {
noSanitize?: boolean;
}
export function renderMarkdown(str?: string): string {
return marked(str || '');
export function renderMarkdown(str?: string, options?: RenderMarkdownOptions): string {
if (!hasInitialized) {
marked.setOptions({
pedantic: false,
gfm: true,
smartLists: true,
smartypants: false,
xhtml: false,
});
hasInitialized = true;
}
const html = marked(str || '');
if (options?.noSanitize) {
return html;
}
return sanitize(html);
}
@@ -130,12 +130,13 @@ describe('Stats Calculators', () => {
const stats = reduceField({
field: createField('x', info[0].data),
reducers: [ReducerID.first, ReducerID.last, ReducerID.firstNotNull, ReducerID.lastNotNull], // uses standard path
reducers: [ReducerID.first, ReducerID.last, ReducerID.firstNotNull, ReducerID.lastNotNull, ReducerID.diffperc], // uses standard path
});
expect(stats[ReducerID.first]).toEqual(null);
expect(stats[ReducerID.last]).toEqual(null);
expect(stats[ReducerID.firstNotNull]).toEqual(200);
expect(stats[ReducerID.lastNotNull]).toEqual(200);
expect(stats[ReducerID.diffperc]).toEqual(0);
const reducers = [ReducerID.lastNotNull, ReducerID.firstNotNull];
for (const input of info) {
@@ -15,6 +15,7 @@ export enum ReducerID {
count = 'count',
range = 'range',
diff = 'diff',
diffperc = 'diffperc',
delta = 'delta',
step = 'step',
@@ -223,6 +224,12 @@ export const fieldReducers = new Registry<FieldReducerInfo>(() => [
standard: false,
reduce: calculateDistinctCount,
},
{
id: ReducerID.diffperc,
name: 'Difference percent',
description: 'Percentage difference between first and last values',
standard: true,
},
]);
export function doStandardCalcs(field: Field, ignoreNulls: boolean, nullAsZero: boolean): FieldCalcs {
@@ -244,6 +251,7 @@ export function doStandardCalcs(field: Field, ignoreNulls: boolean, nullAsZero:
diff: null,
delta: 0,
step: Number.MAX_VALUE,
diffperc: 0,
// Just used for calculations -- not exposed as a stat
previousDeltaUp: true,
@@ -353,6 +361,9 @@ export function doStandardCalcs(field: Field, ignoreNulls: boolean, nullAsZero:
calcs.diff = calcs.lastNotNull - calcs.firstNotNull;
}
if (isNumber(calcs.firstNotNull) && isNumber(calcs.diff)) {
calcs.diffperc = calcs.diff / calcs.firstNotNull;
}
return calcs;
}
@@ -1,6 +1,11 @@
import { ComponentType } from 'react';
import { RegistryItem, Registry } from '../utils/Registry';
import { NumberFieldConfigSettings, SelectFieldConfigSettings, StringFieldConfigSettings } from '../field';
import {
NumberFieldConfigSettings,
SliderFieldConfigSettings,
SelectFieldConfigSettings,
StringFieldConfigSettings,
} from '../field';
/**
* Option editor registry item
@@ -71,6 +76,10 @@ export interface OptionsUIRegistryBuilderAPI<
config: OptionEditorConfig<TOptions, TSettings, number>
): this;
addSliderInput?<TSettings extends SliderFieldConfigSettings = SliderFieldConfigSettings>(
config: OptionEditorConfig<TOptions, TSettings, number>
): this;
addTextInput?<TSettings extends StringFieldConfigSettings = StringFieldConfigSettings>(
config: OptionEditorConfig<TOptions, TSettings, string>
): this;
@@ -1,13 +0,0 @@
import { eventFactory } from './utils';
export interface AppEvent<T> {
readonly name: string;
payload?: T;
}
export type AlertPayload = [string, string?];
export type AlertErrorPayload = [string, (string | Error)?];
export const alertSuccess = eventFactory<AlertPayload>('alert-success');
export const alertWarning = eventFactory<AlertPayload>('alert-warning');
export const alertError = eventFactory<AlertErrorPayload>('alert-error');
@@ -55,6 +55,8 @@ export interface LicenseInfo {
expiry: number;
licenseUrl: string;
stateInfo: string;
hasValidLicense: boolean;
edition: string;
}
/**
+8
View File
@@ -6,6 +6,7 @@ export type KeyValue<T = any> = Record<string, T>;
/**
* Represent panel data loading state.
* @public
*/
export enum LoadingState {
NotStarted = 'NotStarted',
@@ -21,6 +22,9 @@ export enum DataTopic {
export type PreferredVisualisationType = 'graph' | 'table' | 'logs' | 'trace';
/**
* @public
*/
export interface QueryResultMeta {
/** DatasSource Specific Values */
custom?: Record<string, any>;
@@ -67,6 +71,7 @@ export interface QueryResultMetaStat extends FieldConfig {
/**
* QueryResultMetaNotice is a structure that provides user notices for query result data
* @public
*/
export interface QueryResultMetaNotice {
/**
@@ -91,6 +96,9 @@ export interface QueryResultMetaNotice {
inspect?: 'meta' | 'error' | 'data' | 'stats';
}
/**
* @public
*/
export interface QueryResultBase {
/**
* Matches the query target refId
@@ -114,6 +114,7 @@ export interface DataSourcePluginMeta<T extends KeyValue = {}> extends PluginMet
queryOptions?: PluginMetaQueryOptions;
sort?: number;
streaming?: boolean;
unlicensed?: boolean;
}
interface PluginMetaQueryOptions {
@@ -499,6 +500,7 @@ export interface QueryHint {
export interface MetricFindValue {
text: string;
value?: string | number;
expandable?: boolean;
}
@@ -599,6 +601,6 @@ export abstract class LanguageProvider {
* Returns startTask that resolves with a task list when main syntax is loaded.
* Task list consists of secondary promises that load more detailed language features.
*/
abstract start: () => Promise<any[]>;
abstract start: () => Promise<Array<Promise<any>>>;
startTask?: Promise<any[]>;
}
+1 -7
View File
@@ -7,7 +7,6 @@ export * from './navModel';
export * from './select';
export * from './time';
export * from './thresholds';
export * from './utils';
export * from './valueMapping';
export * from './displayValue';
export * from './graph';
@@ -27,12 +26,7 @@ export * from './orgs';
export * from './flot';
export * from './trace';
export * from './explore';
export * from './legacyEvents';
export * from './live';
import * as AppEvents from './appEvents';
import { AppEvent } from './appEvents';
export { AppEvent, AppEvents };
import * as PanelEvents from './panelEvents';
export { PanelEvents };
export { GrafanaConfig, BuildInfo, FeatureToggles, LicenseInfo } from './config';
@@ -0,0 +1,47 @@
import { DataQueryError, DataQueryResponseData } from './datasource';
import { AngularPanelMenuItem } from './panel';
import { DataFrame } from './dataFrame';
import { eventFactory } from '../events/eventFactory';
import { BusEventBase, BusEventWithPayload } from '../events/types';
export type AlertPayload = [string, string?];
export type AlertErrorPayload = [string, (string | Error)?];
export const AppEvents = {
alertSuccess: eventFactory<AlertPayload>('alert-success'),
alertWarning: eventFactory<AlertPayload>('alert-warning'),
alertError: eventFactory<AlertErrorPayload>('alert-error'),
};
export const PanelEvents = {
refresh: eventFactory('refresh'),
componentDidMount: eventFactory('component-did-mount'),
dataReceived: eventFactory<DataQueryResponseData[]>('data-received'),
dataError: eventFactory<DataQueryError>('data-error'),
dataFramesReceived: eventFactory<DataFrame[]>('data-frames-received'),
dataSnapshotLoad: eventFactory<DataQueryResponseData[]>('data-snapshot-load'),
editModeInitialized: eventFactory('init-edit-mode'),
initPanelActions: eventFactory<AngularPanelMenuItem[]>('init-panel-actions'),
panelInitialized: eventFactory('panel-initialized'),
panelSizeChanged: eventFactory('panel-size-changed'),
panelTeardown: eventFactory('panel-teardown'),
render: eventFactory<any>('render'),
};
/** @public */
export interface LegacyGraphHoverEventPayload {
pos: any;
panel: {
id: number;
};
}
/** @alpha */
export class LegacyGraphHoverEvent extends BusEventWithPayload<LegacyGraphHoverEventPayload> {
static type = 'graph-hover';
}
/** @alpha */
export class LegacyGraphHoverClearEvent extends BusEventBase {
static type = 'graph-hover-clear';
}
+4
View File
@@ -4,6 +4,7 @@ import { ScopedVars } from './ScopedVars';
import { LoadingState } from './data';
import { DataFrame } from './dataFrame';
import { AbsoluteTimeRange, TimeRange, TimeZone } from './time';
import { EventBus } from '../events';
import { FieldConfigSource } from './fieldOverrides';
import { Registry } from '../utils';
import { StandardEditorProps } from '../field';
@@ -77,6 +78,9 @@ export interface PanelProps<T = any> {
/** Panel title */
title: string;
/** EventBus */
eventBus: EventBus;
/** Panel options change handler */
onOptionsChange: (options: T) => void;
@@ -1,28 +0,0 @@
import { eventFactory } from './utils';
import { DataQueryError, DataQueryResponseData } from './datasource';
import { AngularPanelMenuItem } from './panel';
import { DataFrame } from './dataFrame';
/** Payloads */
export interface PanelChangeViewPayload {
fullscreen?: boolean;
edit?: boolean;
panelId?: number;
toggle?: boolean;
}
/** Events */
export const refresh = eventFactory('refresh');
export const componentDidMount = eventFactory('component-did-mount');
export const dataError = eventFactory<DataQueryError>('data-error');
export const dataReceived = eventFactory<DataQueryResponseData[]>('data-received');
export const dataFramesReceived = eventFactory<DataFrame[]>('data-frames-received');
export const dataSnapshotLoad = eventFactory<DataQueryResponseData[]>('data-snapshot-load');
export const editModeInitialized = eventFactory('init-edit-mode');
export const initPanelActions = eventFactory<AngularPanelMenuItem[]>('init-panel-actions');
export const panelChangeView = eventFactory<PanelChangeViewPayload>('panel-change-view');
export const panelInitialized = eventFactory('panel-initialized');
export const panelSizeChanged = eventFactory('panel-size-changed');
export const panelTeardown = eventFactory('panel-teardown');
export const render = eventFactory<any>('render');
export const viewModeChanged = eventFactory('view-mode-changed');
+18 -2
View File
@@ -2,12 +2,14 @@ import { ComponentClass } from 'react';
import { KeyValue } from './data';
import { LiveChannelSupport } from './live';
/** Describes plugins life cycle status */
export enum PluginState {
alpha = 'alpha', // Only included it `enable_alpha` is true
alpha = 'alpha', // Only included if `enable_alpha` config option is true
beta = 'beta', // Will show a warning banner
deprecated = 'deprecated', // Will continue to work -- but not show up in the options to add
}
/** Describes {@link https://grafana.com/docs/grafana/latest/plugins | type of plugin} */
export enum PluginType {
panel = 'panel',
datasource = 'datasource',
@@ -15,12 +17,26 @@ export enum PluginType {
renderer = 'renderer',
}
/** Describes status of {@link https://grafana.com/docs/grafana/latest/plugins/plugin-signatures/ | plugin signature} */
export enum PluginSignatureStatus {
internal = 'internal', // core plugin, no signature
valid = 'valid', // signed and accurate MANIFEST
invalid = 'invalid', // invalid signature
modified = 'modified', // valid signature, but content mismatch
unsigned = 'unsigned', // no MANIFEST file
missing = 'missing', // missing signature file
}
/** Describes error code returned from Grafana plugins API call */
export enum PluginErrorCode {
missingSignature = 'signatureMissing',
invalidSignature = 'signatureInvalid',
modifiedSignature = 'signatureModified',
}
/** Describes error returned from Grafana plugins API call */
export interface PluginError {
errorCode: PluginErrorCode;
pluginId: string;
}
export interface PluginMeta<T extends KeyValue = {}> {
@@ -12,12 +12,11 @@ import {
StandardEditorProps,
StringFieldConfigSettings,
NumberFieldConfigSettings,
ColorFieldConfigSettings,
SliderFieldConfigSettings,
identityOverrideProcessor,
UnitFieldConfigSettings,
unitOverrideProcessor,
} from '../field';
import { FieldColor } from '../types';
/**
* Fluent API for declarative creation of field config option editors
@@ -39,6 +38,18 @@ export class FieldConfigEditorBuilder<TOptions> extends OptionsUIRegistryBuilder
});
}
addSliderInput<TSettings>(config: FieldConfigEditorConfig<TOptions, TSettings & SliderFieldConfigSettings, number>) {
return this.addCustomEditor({
...config,
id: config.path,
override: standardEditorsRegistry.get('slider').editor as any,
editor: standardEditorsRegistry.get('slider').editor as any,
process: numberOverrideProcessor,
shouldApply: config.shouldApply ? config.shouldApply : field => field.type === FieldType.number,
settings: config.settings || {},
});
}
addTextInput<TSettings>(config: FieldConfigEditorConfig<TOptions, TSettings & StringFieldConfigSettings, string>) {
return this.addCustomEditor({
...config,
@@ -91,9 +102,7 @@ export class FieldConfigEditorBuilder<TOptions> extends OptionsUIRegistryBuilder
});
}
addColorPicker<TSettings = any>(
config: FieldConfigEditorConfig<TOptions, TSettings & ColorFieldConfigSettings, FieldColor>
) {
addColorPicker<TSettings = any>(config: FieldConfigEditorConfig<TOptions, TSettings, string>) {
return this.addCustomEditor({
...config,
id: config.path,
@@ -136,6 +145,14 @@ export class PanelOptionsEditorBuilder<TOptions> extends OptionsUIRegistryBuilde
});
}
addSliderInput<TSettings>(config: PanelOptionsEditorConfig<TOptions, TSettings & SliderFieldConfigSettings, number>) {
return this.addCustomEditor({
...config,
id: config.path,
editor: standardEditorsRegistry.get('slider').editor as any,
});
}
addTextInput<TSettings>(config: PanelOptionsEditorConfig<TOptions, TSettings & StringFieldConfigSettings, string>) {
return this.addCustomEditor({
...config,
@@ -182,9 +199,7 @@ export class PanelOptionsEditorBuilder<TOptions> extends OptionsUIRegistryBuilde
});
}
addColorPicker<TSettings = any>(
config: PanelOptionsEditorConfig<TOptions, TSettings & ColorFieldConfigSettings, string>
): this {
addColorPicker<TSettings = any>(config: PanelOptionsEditorConfig<TOptions, TSettings, string>): this {
return this.addCustomEditor({
...config,
id: config.path,
@@ -5,6 +5,7 @@ import {
dateTimeAsUS,
dateTimeAsUSNoDateIfToday,
getDateTimeAsLocalFormat,
getDateTimeAsLocalFormatNoDateIfToday,
dateTimeFromNow,
toClockMilliseconds,
toClockSeconds,
@@ -127,6 +128,7 @@ export const getCategories = (): ValueFormatCategory[] => [
{ name: 'Indian Rupee (₹)', id: 'currencyINR', fn: currency('₹') },
{ name: 'South Korean Won (₩)', id: 'currencyKRW', fn: currency('₩') },
{ name: 'Indonesian Rupiah (Rp)', id: 'currencyIDR', fn: currency('Rp') },
{ name: 'Philippine Peso (PHP)', id: 'currencyPHP', fn: currency('PHP') },
],
},
{
@@ -186,6 +188,11 @@ export const getCategories = (): ValueFormatCategory[] => [
{ name: 'Datetime US', id: 'dateTimeAsUS', fn: dateTimeAsUS },
{ name: 'Datetime US (No date if today)', id: 'dateTimeAsUSNoDateIfToday', fn: dateTimeAsUSNoDateIfToday },
{ name: 'Datetime local', id: 'dateTimeAsLocal', fn: getDateTimeAsLocalFormat() },
{
name: 'Datetime local (No date if today)',
id: 'dateTimeAsLocalNoDateIfToday',
fn: getDateTimeAsLocalFormatNoDateIfToday(),
},
{ name: 'Datetime default', id: 'dateTimeAsSystem', fn: dateTimeSystemFormatter },
{ name: 'From Now', id: 'dateTimeFromNow', fn: dateTimeFromNow },
],
@@ -3,6 +3,8 @@ import {
dateTimeAsIsoNoDateIfToday,
dateTimeAsUS,
dateTimeAsUSNoDateIfToday,
getDateTimeAsLocalFormat,
getDateTimeAsLocalFormatNoDateIfToday,
dateTimeFromNow,
Interval,
toClock,
@@ -74,6 +76,36 @@ describe('date time formats', () => {
expect(actual.text).toBe(expected);
});
it('should format as local date', () => {
const dateTimeObject = browserTime.toDate();
const formattedDateText = getDateTimeAsLocalFormat()(epoch, 0, 0).text;
expect(formattedDateText).toContain(dateTimeObject.getFullYear());
expect(formattedDateText).toContain(dateTimeObject.getSeconds());
});
it('should format as local date and skip date when today', () => {
const now = dateTime();
const dateTimeObject = now.toDate();
const formattedDateText = getDateTimeAsLocalFormatNoDateIfToday()(now.valueOf(), 0, 0).text;
expect(formattedDateText).not.toContain(dateTimeObject.getFullYear());
expect(formattedDateText).toContain(dateTimeObject.getSeconds());
});
it('should format as local date (in UTC)', () => {
const dateTimeObject = utcTime.toDate();
const formattedDateText = getDateTimeAsLocalFormat()(epoch, 0, 0, 'utc').text;
expect(formattedDateText).toContain(dateTimeObject.getFullYear());
expect(formattedDateText).toContain(dateTimeObject.getSeconds());
});
it('should format as local date (in UTC) and skip date when today', () => {
const now = toUtc();
const dateTimeObject = now.toDate();
const formattedDateText = getDateTimeAsLocalFormatNoDateIfToday()(now.valueOf(), 0, 0, 'utc').text;
expect(formattedDateText).not.toContain(dateTimeObject.getFullYear());
expect(formattedDateText).toContain(dateTimeObject.getSeconds());
});
it('should format as from now with days', () => {
const daysAgo = dateTime().add(-7, 'd');
const expected = '7 days ago';
@@ -383,6 +383,24 @@ export function getDateTimeAsLocalFormat() {
);
}
export function getDateTimeAsLocalFormatNoDateIfToday() {
return toDateTimeValueFormatter(
localTimeFormat({
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
}),
localTimeFormat({
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
})
);
}
export function dateTimeSystemFormatter(
value: number,
decimals: DecimalCount,

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