Merge branch 'main' into mmandrus/per-panel-cache-ttl
This commit is contained in:
+8
-20
@@ -3,14 +3,6 @@
|
||||
// If this file contains merge conflicts, use `betterer merge` to automatically resolve them:
|
||||
// https://phenomnomnominal.github.io/betterer/docs/results-file/#merge
|
||||
//
|
||||
exports[`no enzyme tests`] = {
|
||||
value: `{
|
||||
"packages/grafana-ui/src/components/QueryField/QueryField.test.tsx:2976628669": [
|
||||
[0, 26, 13, "RegExp match", "2409514259"]
|
||||
]
|
||||
}`
|
||||
};
|
||||
|
||||
exports[`better eslint`] = {
|
||||
value: `{
|
||||
"e2e/benchmarks/live/4-20hz-panels.spec.ts:5381": [
|
||||
@@ -277,8 +269,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"packages/grafana-data/src/geo/layer.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"packages/grafana-data/src/panel/PanelPlugin.test.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
@@ -584,8 +575,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "6"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
|
||||
],
|
||||
"packages/grafana-data/src/types/variables.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
@@ -993,10 +983,14 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "3"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "4"]
|
||||
],
|
||||
"packages/grafana-schema/src/veneer/common.types.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"packages/grafana-schema/src/veneer/dashboard.types.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "3"]
|
||||
],
|
||||
"packages/grafana-toolkit/src/cli/tasks/component.create.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
@@ -1313,12 +1307,6 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"packages/grafana-ui/src/components/QueryField/QueryField.test.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"]
|
||||
],
|
||||
"packages/grafana-ui/src/components/QueryField/QueryField.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "1"]
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { regexp } from '@betterer/regexp';
|
||||
import { BettererFileTest } from '@betterer/betterer';
|
||||
import { ESLint, Linter } from 'eslint';
|
||||
import { existsSync } from 'fs';
|
||||
import { exec } from 'child_process';
|
||||
import path from 'path';
|
||||
import glob from 'glob';
|
||||
|
||||
export default {
|
||||
'no enzyme tests': () => regexp(/from 'enzyme'/g).include('**/*.test.*'),
|
||||
'better eslint': () => countEslintErrors().include('**/*.{ts,tsx}'),
|
||||
'no undocumented stories': () => countUndocumentedStories().include('**/*.story.tsx'),
|
||||
};
|
||||
|
||||
@@ -23,6 +23,12 @@ $(BRA): $(BINGO_DIR)/bra.mod
|
||||
@echo "(re)installing $(GOBIN)/bra-v0.0.0-20200517080246-1e3013ecaff8"
|
||||
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=bra.mod -o=$(GOBIN)/bra-v0.0.0-20200517080246-1e3013ecaff8 "github.com/unknwon/bra"
|
||||
|
||||
CUE := $(GOBIN)/cue-v0.5.0-beta.2
|
||||
$(CUE): $(BINGO_DIR)/cue.mod
|
||||
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
|
||||
@echo "(re)installing $(GOBIN)/cue-v0.5.0-beta.2"
|
||||
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=cue.mod -o=$(GOBIN)/cue-v0.5.0-beta.2 "cuelang.org/go/cmd/cue"
|
||||
|
||||
DRONE := $(GOBIN)/drone-v1.5.0
|
||||
$(DRONE): $(BINGO_DIR)/drone.mod
|
||||
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT
|
||||
|
||||
go 1.19
|
||||
|
||||
require cuelang.org/go v0.5.0-beta.2 // cmd/cue
|
||||
@@ -0,0 +1,30 @@
|
||||
cuelang.org/go v0.4.3 h1:W3oBBjDTm7+IZfCKZAmC8uDG0eYfJL4Pp/xbbCMKaVo=
|
||||
cuelang.org/go v0.4.3/go.mod h1:7805vR9H+VoBNdWFdI7jyDR3QLUPp4+naHfbcgp55HI=
|
||||
cuelang.org/go v0.5.0-beta.2 h1:am5M7jGvNTJ0rnjrFNyvE7fucL/wRqb0emK4XxdThQI=
|
||||
cuelang.org/go v0.5.0-beta.2/go.mod h1:okjJBHFQFer+a41sAe2SaGm1glWS8oEb6CmJvn5Zdws=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd/v2 v2.0.1 h1:y1Rh3tEU89D+7Tgbw+lp52T6p/GJLpDmNvr10UWqLTE=
|
||||
github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E=
|
||||
github.com/emicklei/proto v1.6.15 h1:XbpwxmuOPrdES97FrSfpyy67SSCV/wBIKXqgJzh6hNw=
|
||||
github.com/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de h1:D5x39vF5KCwKQaw+OC9ZPiLVHXz3UFw2+psEX+gYcto=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20201118171849-f6a6b3f636fc h1:gSVONBi2HWMFXCa9jFdYvYk7IwW/mTLxWOF7rXS4LO0=
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b h1:zd/2RNzIRkoGGMjE+YIsZ85CnDIz672JK2F3Zl4vux4=
|
||||
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449 h1:xUIPaMhvROX9dhPvRCenIJtU78+lbEenGbgqB5hfHCQ=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220818022119-ed83ed61efb9 h1:VtCrPQXM5Wo9l7XN64SjBMczl48j8mkP+2e3OhYlz+0=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
|
||||
golang.org/x/tools v0.0.0-20200612220849-54c614fe050c h1:g6oFfz6Cmw68izP3xsdud3Oxu145IPkeFzyRg58AKHM=
|
||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
@@ -10,6 +10,8 @@ fi
|
||||
|
||||
BRA="${GOBIN}/bra-v0.0.0-20200517080246-1e3013ecaff8"
|
||||
|
||||
CUE="${GOBIN}/cue-v0.5.0-beta.2"
|
||||
|
||||
DRONE="${GOBIN}/drone-v1.5.0"
|
||||
|
||||
GOLANGCI_LINT="${GOBIN}/golangci-lint-v1.50.1"
|
||||
|
||||
Vendored
+8
@@ -27,6 +27,14 @@
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"port": 9229
|
||||
},
|
||||
{
|
||||
"name": "Debug Go test",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "test",
|
||||
"program": "${workspaceFolder}/${relativeFileDirname}",
|
||||
"showLog": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -7,12 +7,13 @@ WIRE_TAGS = "oss"
|
||||
-include local/Makefile
|
||||
include .bingo/Variables.mk
|
||||
|
||||
.PHONY: all deps-go deps-js deps build-go build-backend build-server build-cli build-js build build-docker-full build-docker-full-ubuntu lint-go golangci-lint test-go test-js gen-ts test run run-frontend clean devenv devenv-down protobuf drone help gen-go gen-cue
|
||||
.PHONY: all deps-go deps-js deps build-go build-backend build-server build-cli build-js build build-docker-full build-docker-full-ubuntu lint-go golangci-lint test-go test-js gen-ts test run run-frontend clean devenv devenv-down protobuf drone help gen-go gen-cue fix-cue
|
||||
|
||||
GO = go
|
||||
GO_FILES ?= ./pkg/...
|
||||
SH_FILES ?= $(shell find ./scripts -name *.sh)
|
||||
GO_BUILD_FLAGS += $(if $(GO_BUILD_DEV),-dev)
|
||||
GO_BUILD_FLAGS += $(if $(GO_BUILD_DEV),-dev)
|
||||
GO_BUILD_FLAGS += $(if $(GO_BUILD_TAGS),-build-tags=$(GO_BUILD_TAGS))
|
||||
|
||||
all: deps build
|
||||
@@ -75,6 +76,11 @@ gen-go: $(WIRE) gen-cue
|
||||
@echo "generate go files"
|
||||
$(WIRE) gen -tags $(WIRE_TAGS) ./pkg/server ./pkg/cmd/grafana-cli/runner
|
||||
|
||||
fix-cue: $(CUE)
|
||||
@echo "formatting cue files"
|
||||
$(CUE) fix kinds/**/*.cue
|
||||
$(CUE) fix public/app/plugins/**/**/*.cue
|
||||
|
||||
gen-jsonnet:
|
||||
go generate ./devenv/jsonnet
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ services:
|
||||
logging: *default-logging
|
||||
|
||||
tempo:
|
||||
image: grafana/tempo:latest
|
||||
image: grafana/tempo:main-9a8474f
|
||||
command:
|
||||
- --config.file=/etc/tempo.yaml
|
||||
- --search.enabled=true
|
||||
|
||||
@@ -18,11 +18,6 @@ distributor:
|
||||
grpc:
|
||||
opencensus:
|
||||
|
||||
ingester:
|
||||
trace_idle_period: 10s # the length of time after a trace has not received spans to consider it complete and flush it
|
||||
max_block_bytes: 1_000_000 # cut the head block when it hits this size or ...
|
||||
max_block_duration: 5m # this much time passes
|
||||
|
||||
compactor:
|
||||
compaction:
|
||||
compaction_window: 1h # blocks in this time window will be compacted together
|
||||
@@ -41,26 +36,19 @@ metrics_generator:
|
||||
- url: http://prometheus:9090/api/v1/write
|
||||
send_exemplars: true
|
||||
|
||||
query_frontend:
|
||||
search:
|
||||
max_duration: 0 # allow searches >1h
|
||||
|
||||
storage:
|
||||
trace:
|
||||
backend: local # backend configuration to use
|
||||
block:
|
||||
bloom_filter_false_positive: .05 # bloom filter false positive rate. lower values create larger filters but fewer false positives
|
||||
index_downsample_bytes: 1000 # number of bytes per index record
|
||||
encoding: zstd # block encoding/compression. options: none, gzip, lz4-64k, lz4-256k, lz4-1M, lz4, snappy, zstd, s2
|
||||
v2_index_downsample_bytes: 1000 # number of bytes per index record
|
||||
v2_encoding: zstd # block encoding/compression. options: none, gzip, lz4-64k, lz4-256k, lz4-1M, lz4, snappy, zstd, s2
|
||||
version: vParquet
|
||||
wal:
|
||||
path: /tmp/tempo/wal # where to store the the wal locally
|
||||
encoding: snappy # wal encoding/compression. options: none, gzip, lz4-64k, lz4-256k, lz4-1M, lz4, snappy, zstd, s2
|
||||
v2_encoding: snappy # wal encoding/compression. options: none, gzip, lz4-64k, lz4-256k, lz4-1M, lz4, snappy, zstd, s2
|
||||
local:
|
||||
path: /tmp/tempo/blocks
|
||||
pool:
|
||||
max_workers: 100 # worker pool determines the number of parallel requests to the object store backend
|
||||
queue_depth: 10000
|
||||
|
||||
overrides:
|
||||
metrics_generator_processors: [service-graphs, span-metrics]
|
||||
|
||||
@@ -21,6 +21,16 @@ Grafana refers to such variables as template variables.
|
||||
|
||||
For an introduction to templating and template variables, refer to the [Templating]({{< relref "../../../dashboards/variables" >}}) and [Add and manage variables]({{< relref "../../../dashboards/variables/add-template-variables" >}}) documentation.
|
||||
|
||||
## Select a query type
|
||||
|
||||
There are three query types for Graphite template variables
|
||||
|
||||
| Query Type | Description |
|
||||
| ----------------- | ------------------------------------------------------------------------------- |
|
||||
| Default Query | Use functions such as `tags()`, `tag_values()`, `expand(<metric>)` and metrics. |
|
||||
| Value Query | Returns all the values for a query that includes a metric and function. |
|
||||
| Metric Name Query | Returns all the names for a query that includes a metric and function. |
|
||||
|
||||
## Use tag variables
|
||||
|
||||
To create a variable using tag values, use the Grafana functions `tags` and `tag_values`.
|
||||
@@ -40,7 +50,7 @@ tag_values(server, server=~backend\*, app=~${apps:regex})
|
||||
|
||||
For details, refer to the [Graphite docs on the autocomplete API for tags](http://graphite.readthedocs.io/en/latest/tags.html#auto-complete-support).
|
||||
|
||||
### Use multi-valie variables in tag queries
|
||||
### Use multi-value variables in tag queries
|
||||
|
||||
Multi-value variables in tag queries use the advanced formatting syntax for variables introduced in Grafana v5.0: `{var:regex}`.
|
||||
Non-tag queries use the default glob formatting for multi-value variables.
|
||||
|
||||
@@ -99,7 +99,7 @@ For details, refer to the [Grafana blog](/blog/2020/09/28/new-in-grafana-7.2-__r
|
||||
|
||||
The Prometheus data source supports two variable syntaxes for use in the **Query** field:
|
||||
|
||||
- `$<varname>`, for example `rate(http_requests_total{job=~"\$job"}[$_rate_interval])`, which is easier to read and write but does not allow you to use a variable in the middle of a word.
|
||||
- `$<varname>`, for example `rate(http_requests_total{job=~"$job"}[$_rate_interval])`, which is easier to read and write but does not allow you to use a variable in the middle of a word.
|
||||
- `[[varname]]`, for example `rate(http_requests_total{job=~"[[job]]"}[$_rate_interval])`
|
||||
|
||||
If you've enabled the _Multi-value_ or _Include all value_ options, Grafana converts the labels from plain text to a regex-compatible string, which requires you to use `=~` instead of `=`.
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
keywords:
|
||||
- grafana
|
||||
- schema
|
||||
title: AlertGroupsPanelCfg kind
|
||||
---
|
||||
> Both documentation generation and kinds schemas are in active development and subject to change without prior notice.
|
||||
|
||||
# AlertGroupsPanelCfg kind
|
||||
|
||||
## Maturity: merged
|
||||
## Version: 0.0
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
|----------------|-------------------------|----------|-------------|
|
||||
| `PanelOptions` | [object](#paneloptions) | **Yes** | |
|
||||
|
||||
## PanelOptions
|
||||
|
||||
### Properties
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
|----------------|---------|----------|-------------------------------------------------------------|
|
||||
| `alertmanager` | string | **Yes** | Name of the alertmanager used as a source for alerts |
|
||||
| `expandAll` | boolean | **Yes** | Expand all alert groups by default |
|
||||
| `labels` | string | **Yes** | Comma-separated list of values used to filter alert results |
|
||||
|
||||
|
||||
+2
-2
@@ -8,8 +8,8 @@ title: AnnotationsListPanelCfg kind
|
||||
|
||||
# AnnotationsListPanelCfg kind
|
||||
|
||||
### Maturity: experimental
|
||||
### Version: 0.0
|
||||
## Maturity: experimental
|
||||
## Version: 0.0
|
||||
|
||||
## Properties
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ title: BarChartPanelCfg kind
|
||||
|
||||
# BarChartPanelCfg kind
|
||||
|
||||
### Maturity: experimental
|
||||
### Version: 0.0
|
||||
## Maturity: experimental
|
||||
## Version: 0.0
|
||||
|
||||
## Properties
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ title: BarGaugePanelCfg kind
|
||||
|
||||
# BarGaugePanelCfg kind
|
||||
|
||||
### Maturity: experimental
|
||||
### Version: 0.0
|
||||
## Maturity: experimental
|
||||
## Version: 0.0
|
||||
|
||||
## Properties
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ title: DashboardListPanelCfg kind
|
||||
|
||||
# DashboardListPanelCfg kind
|
||||
|
||||
### Maturity: experimental
|
||||
### Version: 0.0
|
||||
## Maturity: experimental
|
||||
## Version: 0.0
|
||||
|
||||
## Properties
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ title: GaugePanelCfg kind
|
||||
|
||||
# GaugePanelCfg kind
|
||||
|
||||
### Maturity: experimental
|
||||
### Version: 0.0
|
||||
## Maturity: experimental
|
||||
## Version: 0.0
|
||||
|
||||
## Properties
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ title: HistogramPanelCfg kind
|
||||
|
||||
# HistogramPanelCfg kind
|
||||
|
||||
### Maturity: experimental
|
||||
### Version: 0.0
|
||||
## Maturity: experimental
|
||||
## Version: 0.0
|
||||
|
||||
## Properties
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ title: NewsPanelCfg kind
|
||||
|
||||
# NewsPanelCfg kind
|
||||
|
||||
### Maturity: experimental
|
||||
### Version: 0.0
|
||||
## Maturity: experimental
|
||||
## Version: 0.0
|
||||
|
||||
## Properties
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ title: PieChartPanelCfg kind
|
||||
|
||||
# PieChartPanelCfg kind
|
||||
|
||||
### Maturity: experimental
|
||||
### Version: 0.0
|
||||
## Maturity: experimental
|
||||
## Version: 0.0
|
||||
|
||||
## Properties
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ title: StatPanelCfg kind
|
||||
|
||||
# StatPanelCfg kind
|
||||
|
||||
### Maturity: experimental
|
||||
### Version: 0.0
|
||||
## Maturity: experimental
|
||||
## Version: 0.0
|
||||
|
||||
## Properties
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ title: TextPanelCfg kind
|
||||
|
||||
# TextPanelCfg kind
|
||||
|
||||
### Maturity: experimental
|
||||
### Version: 0.0
|
||||
## Maturity: experimental
|
||||
## Version: 0.0
|
||||
|
||||
## Properties
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ title: Dashboard kind
|
||||
|
||||
# Dashboard kind
|
||||
|
||||
### Maturity: experimental
|
||||
### Version: 0.0
|
||||
## Maturity: experimental
|
||||
## Version: 0.0
|
||||
|
||||
## Properties
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ title: Playlist kind
|
||||
|
||||
# Playlist kind
|
||||
|
||||
### Maturity: merged
|
||||
### Version: 0.0
|
||||
## Maturity: merged
|
||||
## Version: 0.0
|
||||
|
||||
## Properties
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ title: Team kind
|
||||
|
||||
# Team kind
|
||||
|
||||
### Maturity: merged
|
||||
### Version: 0.0
|
||||
## Maturity: merged
|
||||
## Version: 0.0
|
||||
|
||||
## Properties
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ Before you submit your plugin, we ask that you read our guidelines and frequentl
|
||||
|
||||
To speed up the time it takes to review your plugin:
|
||||
|
||||
- Get familiar with our plugin [publishing and signing criteria](publishing-and-signing-criteria.md)
|
||||
- Check that your plugin is ready for review using the [plugin validator](https://github.com/grafana/plugin-validator).
|
||||
- Read our [6 tips for improving your Grafana plugin before you publish](https://grafana.com/blog/2021/01/21/6-tips-for-improving-your-grafana-plugin-before-you-publish/).
|
||||
- Refer to [plugin-examples](https://github.com/grafana/grafana-plugin-examples) to review best practices for building your plugin.
|
||||
|
||||
@@ -19,6 +19,9 @@ Grafana plugins must adhere to the following criteria when being reviewed for pu
|
||||
- Abuse: plugins should not perform actions beyond the scope of the intended use.
|
||||
- Do not include hidden files
|
||||
- Do not manipulate the underlying environment, privileges, or related processes
|
||||
- Security:
|
||||
- Should not access the filesystem
|
||||
- Should not access environment variables
|
||||
|
||||
## Commercial
|
||||
|
||||
|
||||
@@ -141,6 +141,10 @@ after switching to the Logs data source, the query changes to:
|
||||
|
||||
This will return a chunk of logs in the selected time range that can be grepped/text searched.
|
||||
|
||||
### Logs sample
|
||||
|
||||
If the selected data source implements logs sample, and supports both log and metric queries, then for metric queries you will be able to automatically see samples of log lines that contributed to visualized metrics. This feature is currently supported by Loki data sources.
|
||||
|
||||
#### Live tailing
|
||||
|
||||
Use the Live tailing feature to see real-time logs on supported data sources.
|
||||
|
||||
@@ -46,6 +46,7 @@ Some stable features are enabled by default. You can disable a stable feature by
|
||||
| `datasourceLogger` | Logs all datasource requests |
|
||||
| `accessControlOnCall` | Access control primitives for OnCall |
|
||||
| `alertingNoNormalState` | Stop maintaining state of alerts that are not firing |
|
||||
| `topNavCommandPalette` | Launch the Command Palette from the top navigation search box |
|
||||
|
||||
## Alpha feature toggles
|
||||
|
||||
@@ -95,6 +96,7 @@ Alpha features might be changed or removed without prior notice.
|
||||
| `authnService` | Use new auth service to perform authentication |
|
||||
| `sessionRemoteCache` | Enable using remote cache for user sessions |
|
||||
| `alertingBacktesting` | Rule backtesting API for alerting |
|
||||
| `editPanelCSVDragAndDrop` | Enables drag and drop for CSV and Excel files |
|
||||
| `azureMultipleResourcePicker` | Azure multiple resource picker |
|
||||
|
||||
## Development feature toggles
|
||||
|
||||
@@ -13,33 +13,33 @@ weight: 500
|
||||
|
||||
# Upgrade Grafana
|
||||
|
||||
We recommend that you upgrade Grafana often to stay up to date with the latest fixes and enhancements.
|
||||
In order to make this a reality, Grafana upgrades are backward compatible and the upgrade process is simple and quick.
|
||||
We recommend that you upgrade Grafana often to stay current with the latest fixes and enhancements.
|
||||
Because Grafana upgrades are backward compatible, the upgrade process is straightforward. Upgrading between many minor versions and one major version is generally safe, and dashboards and graphs will not change.
|
||||
|
||||
Upgrading between many minor versions and one major version is generally safe, and dashboards and graphs will look the same.
|
||||
There might be minor breaking changes in some releases.
|
||||
We outline these in the [What's New overviews]({{< relref "../whatsnew/" >}}) for each release.
|
||||
For versions of Grafana prior to v9.2, we also published additional information in the [Release Notes]({{< relref "../release-notes/" >}}).
|
||||
We also list all changes, with links to pull requests or issues when available, in the [Changelog](https://github.com/grafana/grafana/blob/main/CHANGELOG.md).
|
||||
In addition to common tasks you should complete for all versions of Grafana, there might be additional upgrade tasks to complete for a version.
|
||||
|
||||
## Backup
|
||||
> **Note:** There might be minor breaking changes in some releases. We outline these in the [What's New ]({{< relref "../whatsnew/" >}}) document for each release.
|
||||
|
||||
We recommend that you backup a few things in case you have to rollback the upgrade.
|
||||
For versions of Grafana prior to v9.2, we published additional information in the [Release Notes]({{< relref "../release-notes/" >}}).
|
||||
|
||||
- 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 configuration options after upgrade and then rollback.
|
||||
When available, we list all changes with links to pull requests or issues in the [Changelog](https://github.com/grafana/grafana/blob/main/CHANGELOG.md).
|
||||
|
||||
### Database backup
|
||||
> **Note:** When possible, we recommend that you test the Grafana upgrade process in a test or development environment.
|
||||
|
||||
Before upgrading it can be a good idea to backup your Grafana database. This will ensure that you can always rollback to your previous version. During startup, Grafana will automatically migrate the database schema (if there are changes or new tables). Sometimes this can cause issues if you later want to downgrade.
|
||||
## Back up the Grafana database
|
||||
|
||||
#### sqlite
|
||||
Although Grafana automatically upgrades the database on startup, we recommend that you back up your Grafana database so that you can roll back to a previous version, if required.
|
||||
|
||||
If you use sqlite you only need to make a backup of your `grafana.db` file. This is usually located at `/var/lib/grafana/grafana.db` on Unix systems.
|
||||
If you are unsure what database you use and where it is stored check you grafana configuration file. If you
|
||||
installed grafana to custom location using a binary tar/zip it is usually in `<grafana_install_dir>/data`.
|
||||
### sqlite
|
||||
|
||||
#### mysql
|
||||
If you use sqlite, you only need to back up the `grafana.db` file. On Unix systems, the database file is usually located in `/var/lib/grafana/`.
|
||||
|
||||
If you are unsure which database you use and where it is stored, check the Grafana configuration file. If you
|
||||
installed Grafana to a custom location using a binary tar/zip, the database is usually located in `<grafana_install_dir>/data`.
|
||||
|
||||
### mysql
|
||||
|
||||
To back up or restore a mysql Grafana database, run the following commands:
|
||||
|
||||
```bash
|
||||
backup:
|
||||
@@ -49,7 +49,9 @@ restore:
|
||||
> mysql -u root -p grafana < grafana_backup.sql
|
||||
```
|
||||
|
||||
#### postgres
|
||||
### postgres
|
||||
|
||||
To back up or restore a postgres Grafana database, run the following commands:
|
||||
|
||||
```bash
|
||||
backup:
|
||||
@@ -59,70 +61,117 @@ restore:
|
||||
> psql grafana < grafana_backup
|
||||
```
|
||||
|
||||
### Ubuntu or Debian
|
||||
## Backup plugins
|
||||
|
||||
You can upgrade Grafana by following the same procedure as when you installed it.
|
||||
We recommend that you back up installed plugins before you upgrade Grafana so that you can roll back to a previous version of Grafana, if necessary.
|
||||
|
||||
#### Upgrade Debian package
|
||||
## Upgrade Grafana
|
||||
|
||||
If you installed Grafana by downloading a Debian package (`.deb`), then you can execute the same `dpkg -i` command but with the new package. It will upgrade your Grafana installation.
|
||||
The following sections provide instructions for how to upgrade Grafana based on your installation method.
|
||||
|
||||
Go to the [download page](https://grafana.com/grafana/download?platform=linux) for the latest download
|
||||
links.
|
||||
### Debian
|
||||
|
||||
```bash
|
||||
wget <debian package url>
|
||||
sudo apt-get install -y adduser
|
||||
sudo dpkg -i grafana_<version>_amd64.deb
|
||||
```
|
||||
To upgrade Grafana installed from a Debian package (`.deb`), complete the following steps:
|
||||
|
||||
#### Upgrade from APT repository
|
||||
1. In your current installation of Grafana, save your custom configuration changes to a file named `<grafana_install_dir>/conf/custom.ini`.
|
||||
|
||||
If you installed Grafana from our APT repository, then Grafana will automatically update when you run apt-get upgrade to upgrade all system packages.
|
||||
This enables you to upgrade Grafana without the risk of losing your configuration changes.
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade
|
||||
```
|
||||
1. [Download](https://grafana.com/grafana/download?platform=linux) the latest version of Grafana.
|
||||
|
||||
#### Upgrade from binary .tar file
|
||||
1. Execute the `dpkg -i` command.
|
||||
|
||||
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.
|
||||
```bash
|
||||
wget <debian package url>
|
||||
sudo apt-get install -y adduser
|
||||
sudo dpkg -i grafana_<version>_amd64.deb
|
||||
```
|
||||
|
||||
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.
|
||||
### APT repository
|
||||
|
||||
### Centos / RHEL
|
||||
To upgrade Grafana installed from the Grafana Labs APT repository, complete the following steps:
|
||||
|
||||
If you installed Grafana by downloading an RPM package you can just follow the same installation guide and execute the same `yum install` or `rpm -i` command but with the new package. It will upgrade your Grafana installation.
|
||||
1. In your current installation of Grafana, save your custom configuration changes to a file named `<grafana_install_dir>/conf/custom.ini`.
|
||||
|
||||
If you used our YUM repository:
|
||||
This enables you to upgrade Grafana without the risk of losing your configuration changes.
|
||||
|
||||
```bash
|
||||
sudo yum update grafana
|
||||
```
|
||||
1. Run the following command.
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade
|
||||
```
|
||||
|
||||
Grafana automatically updates when you run `apt-get upgrade`.
|
||||
|
||||
### Binary .tar file
|
||||
|
||||
To upgrade Grafana installed from the binary `.tar.gz` package, complete the following steps:
|
||||
|
||||
1. In your current installation of Grafana, save your custom configuration changes to a file named `<grafana_install_dir>/conf/custom.ini`.
|
||||
|
||||
This enables you to upgrade Grafana without the risk of losing your configuration changes.
|
||||
|
||||
1. [Download](https://grafana.com/grafana/download) the binary `.tar.gz` package.
|
||||
|
||||
1. Extract the downloaded package and overwrite the existing files.
|
||||
|
||||
### Centos or RHEL
|
||||
|
||||
To upgrade Grafana running on Centos or RHEL, complete the following steps:
|
||||
|
||||
1. In your current installation of Grafana, save your custom configuration changes to a file named `<grafana_install_dir>/conf/custom.ini`.
|
||||
|
||||
This enables you to upgrade Grafana without the risk of losing your configuration changes.
|
||||
|
||||
1. Perform one of the following steps based on your installation.
|
||||
|
||||
- If you [downloaded an RPM package](https://grafana.com/grafana/download) to install Grafana, then complete the steps documented in [Install on RPM-based Linux]({{< relref "./installation/rpm" >}}) to upgrade Grafana.
|
||||
- If you used the Grafana YUM repository, execute the following command:
|
||||
|
||||
```bash
|
||||
sudo yum update grafana
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
This just an example, details depend on how you configured your grafana container.
|
||||
To upgrade Grafana running in a Docker container, complete the following steps:
|
||||
|
||||
```bash
|
||||
docker pull grafana/grafana
|
||||
docker stop my-grafana-container
|
||||
docker rm my-grafana-container
|
||||
docker run -d --name=my-grafana-container --restart=always -v /var/lib/grafana:/var/lib/grafana grafana/grafana
|
||||
```
|
||||
1. In your current installation of Grafana, save your custom configuration changes to a file named `<grafana_install_dir>/conf/custom.ini`.
|
||||
|
||||
This enables you to upgrade Grafana without the risk of losing your configuration changes.
|
||||
|
||||
1. Run a command similar to the following command.
|
||||
|
||||
> **Note:** This is an example. The parameters you enter depend on how you configured your Grafana container.
|
||||
|
||||
```bash
|
||||
docker pull grafana/grafana
|
||||
docker stop my-grafana-container
|
||||
docker rm my-grafana-container
|
||||
docker run -d --name=my-grafana-container --restart=always -v /var/lib/grafana:/var/lib/grafana grafana/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 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.
|
||||
To upgrade Grafana installed on Windows, complete the following steps:
|
||||
|
||||
## Update plugins
|
||||
1. In your current installation of Grafana, save your custom configuration changes to a file named `<grafana_install_dir>/conf/custom.ini`.
|
||||
|
||||
After you have upgraded, we strongly recommend that you update all your plugins as a new version of Grafana
|
||||
This enables you to upgrade Grafana without the risk of losing your configuration changes.
|
||||
|
||||
1. [Download](https://grafana.com/grafana/download) the Windows binary package.
|
||||
|
||||
1. Extract the contents of the package to the location in which you installed Grafana.
|
||||
|
||||
You can overwrite existing files and folders, when prompted.
|
||||
|
||||
## Update Grafana plugins
|
||||
|
||||
After you upgrade Grafana, we recommend that you update all plugins because a new version of Grafana
|
||||
can make older plugins stop working properly.
|
||||
|
||||
You can update all plugins using
|
||||
Run the following command to update plugins:
|
||||
|
||||
```bash
|
||||
grafana-cli plugins update-all
|
||||
|
||||
@@ -277,6 +277,7 @@ require (
|
||||
github.com/Masterminds/semver/v3 v3.1.1
|
||||
github.com/dave/dst v0.27.2
|
||||
github.com/grafana/thema v0.0.0-20230122235053-b4b6714dd1c9
|
||||
github.com/hmarr/codeowners v1.1.1
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f
|
||||
k8s.io/apimachinery v0.25.3
|
||||
)
|
||||
|
||||
@@ -1563,6 +1563,8 @@ github.com/hetznercloud/hcloud-go v1.26.2/go.mod h1:2C5uMtBiMoFr3m7lBFPf7wXTdh33
|
||||
github.com/hetznercloud/hcloud-go v1.32.0/go.mod h1:XX/TQub3ge0yWR2yHWmnDVIrB+MQbda1pHxkUmDlUME=
|
||||
github.com/hetznercloud/hcloud-go v1.33.2 h1:ptWKVYLW7YtjXzsqTFKFxwpVo3iM9UMkVPBYQE4teLU=
|
||||
github.com/hetznercloud/hcloud-go v1.33.2/go.mod h1:XX/TQub3ge0yWR2yHWmnDVIrB+MQbda1pHxkUmDlUME=
|
||||
github.com/hmarr/codeowners v1.1.1 h1:a1eD22I36JYH6O/zb+R+JkJgQ3CSlbu6H9EserOojSw=
|
||||
github.com/hmarr/codeowners v1.1.1/go.mod h1:+ez+YARvfVhzL1MzY0f2+D/VusjODs4iRj3tO/IxBMw=
|
||||
github.com/hodgesds/perf-utils v0.0.8/go.mod h1:F6TfvsbtrF88i++hou29dTXlI2sfsJv+gRZDtmTJkAs=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
|
||||
|
||||
@@ -20,7 +20,6 @@ module.exports = {
|
||||
testTimeout: 30000,
|
||||
resolver: `<rootDir>/public/test/jest-resolver.js`,
|
||||
setupFilesAfterEnv: ['./public/test/setupTests.ts'],
|
||||
snapshotSerializers: ['enzyme-to-json/serializer'],
|
||||
globals: {
|
||||
__webpack_public_path__: '', // empty string
|
||||
},
|
||||
|
||||
@@ -8,7 +8,8 @@ maturity: "experimental"
|
||||
lineage: seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{// 0.0
|
||||
// 0.0
|
||||
{
|
||||
@grafana(TSVeneer="type")
|
||||
|
||||
// Unique numeric identifier for the dashboard.
|
||||
@@ -413,7 +414,7 @@ lineage: seqs: [
|
||||
#MatcherConfig: {
|
||||
id: string | *"" @grafanamaturity(NeedsExpertReview)
|
||||
options?: _ @grafanamaturity(NeedsExpertReview)
|
||||
} @cuetsy(kind="interface")
|
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type")
|
||||
|
||||
#DynamicConfigValue: {
|
||||
id: string | *"" @grafanamaturity(NeedsExpertReview)
|
||||
@@ -506,6 +507,7 @@ lineage: seqs: [
|
||||
}
|
||||
...
|
||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
#HeatmapPanel: {
|
||||
type: "heatmap" @grafanamaturity(NeedsExpertReview)
|
||||
...
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package kind
|
||||
|
||||
name: "Playlist"
|
||||
name: "Playlist"
|
||||
maturity: "merged"
|
||||
|
||||
lineage: seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{//0.0
|
||||
//0.0
|
||||
{
|
||||
// Unique playlist identifier. Generated on creation, either by the
|
||||
// creator of the playlist of by the application.
|
||||
uid: string
|
||||
@@ -39,9 +40,9 @@ lineage: seqs: [
|
||||
value: string
|
||||
|
||||
// Title is an unused property -- it will be removed in the future
|
||||
title?: string
|
||||
title?: string
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package kind
|
||||
|
||||
name: "Team"
|
||||
name: "Team"
|
||||
maturity: "merged"
|
||||
|
||||
lineage: seqs: [
|
||||
@@ -21,7 +21,9 @@ lineage: seqs: [
|
||||
// TODO - it seems it's a team_member.permission, unlikely it should belong to the team kind
|
||||
permission: #Permission @grafanamaturity(ToMetadata="kind", MaybeRemove)
|
||||
// AccessControl metadata associated with a given resource.
|
||||
accessControl?: [string]: bool @grafanamaturity(ToMetadata="sys")
|
||||
accessControl?: {
|
||||
[string]: bool @grafanamaturity(ToMetadata="sys")
|
||||
}
|
||||
// Created indicates when the team was created.
|
||||
created: int64 @grafanamaturity(ToMetadata="sys")
|
||||
// Updated indicates when the team was updated.
|
||||
|
||||
+8
-6
@@ -71,6 +71,12 @@
|
||||
],
|
||||
"*pkg/**/*.go": [
|
||||
"gofmt -w -s"
|
||||
],
|
||||
"*kinds/**/*.cue": [
|
||||
"make fix-cue"
|
||||
],
|
||||
"*public/app/plugins/**/**/*.cue": [
|
||||
"make fix-cue"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -117,8 +123,6 @@
|
||||
"@types/d3-force": "^2.1.0",
|
||||
"@types/d3-scale-chromatic": "1.3.1",
|
||||
"@types/debounce-promise": "3.1.5",
|
||||
"@types/enzyme": "3.10.12",
|
||||
"@types/enzyme-adapter-react-16": "1.0.6",
|
||||
"@types/eslint": "8.4.9",
|
||||
"@types/file-saver": "2.0.5",
|
||||
"@types/glob": "^8.0.0",
|
||||
@@ -161,7 +165,6 @@
|
||||
"@types/uuid": "8.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "5.42.0",
|
||||
"@typescript-eslint/parser": "5.42.0",
|
||||
"@wojtekmaj/enzyme-adapter-react-17": "0.8.0",
|
||||
"autoprefixer": "10.4.13",
|
||||
"babel-jest": "29.3.1",
|
||||
"babel-loader": "9.1.0",
|
||||
@@ -173,8 +176,6 @@
|
||||
"css-loader": "6.7.1",
|
||||
"css-minimizer-webpack-plugin": "4.2.2",
|
||||
"cypress": "9.5.1",
|
||||
"enzyme": "3.11.0",
|
||||
"enzyme-to-json": "3.6.2",
|
||||
"esbuild": "0.16.17",
|
||||
"esbuild-loader": "2.21.0",
|
||||
"esbuild-plugin-browserslist": "^0.6.0",
|
||||
@@ -403,7 +404,8 @@
|
||||
"uuid": "9.0.0",
|
||||
"vendor": "link:./public/vendor",
|
||||
"visjs-network": "4.25.0",
|
||||
"whatwg-fetch": "3.6.2"
|
||||
"whatwg-fetch": "3.6.2",
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.19.1/xlsx-0.19.1.tgz"
|
||||
},
|
||||
"resolutions": {
|
||||
"underscore": "1.13.6",
|
||||
|
||||
@@ -2,74 +2,23 @@ import { Map as OpenLayersMap } from 'ol';
|
||||
import BaseLayer from 'ol/layer/Base';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { MapLayerOptions, FrameGeometrySourceMode } from '@grafana/schema';
|
||||
|
||||
import { EventBus } from '../events';
|
||||
import { GrafanaTheme2 } from '../themes';
|
||||
import { MatcherConfig, PanelData } from '../types';
|
||||
import { PanelData } from '../types';
|
||||
import { PanelOptionsEditorBuilder } from '../utils';
|
||||
import { RegistryItemWithOptions } from '../utils/Registry';
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
* @deprecated use the type from schema
|
||||
*/
|
||||
export enum FrameGeometrySourceMode {
|
||||
Auto = 'auto', // Will scan fields and find best match
|
||||
Geohash = 'geohash',
|
||||
Coords = 'coords', // lon field, lat field
|
||||
Lookup = 'lookup', // keys > location
|
||||
// H3 = 'h3',
|
||||
// WKT = 'wkt,
|
||||
// geojson? geometry text
|
||||
}
|
||||
export { FrameGeometrySourceMode };
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
* @deprecated use the type from schema
|
||||
*/
|
||||
export interface FrameGeometrySource {
|
||||
mode: FrameGeometrySourceMode;
|
||||
|
||||
// Field mappings
|
||||
geohash?: string;
|
||||
latitude?: string;
|
||||
longitude?: string;
|
||||
h3?: string;
|
||||
wkt?: string;
|
||||
lookup?: string;
|
||||
|
||||
// Path to Gazetteer
|
||||
gazetteer?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This gets saved in panel json
|
||||
*
|
||||
* depending on the type, it may have additional config
|
||||
*
|
||||
* This exists in `grafana/data` so the types are well known and extendable but the
|
||||
* layout/frame is control by the map panel
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
export interface MapLayerOptions<TConfig = any> {
|
||||
type: string;
|
||||
name: string; // configured unique display name
|
||||
|
||||
// Custom options depending on the type
|
||||
config?: TConfig;
|
||||
|
||||
// Common method to define geometry fields
|
||||
location?: FrameGeometrySource;
|
||||
|
||||
// Defines which data query refId is associated with the layer
|
||||
filterData?: MatcherConfig;
|
||||
|
||||
// Common properties:
|
||||
// https://openlayers.org/en/latest/apidoc/module-ol_layer_Base-BaseLayer.html
|
||||
// Layer opacity (0-1)
|
||||
opacity?: number;
|
||||
|
||||
// Check tooltip (defaults to true)
|
||||
tooltip?: boolean;
|
||||
}
|
||||
export type { FrameGeometrySource, MapLayerOptions } from '@grafana/schema';
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
@@ -81,7 +30,7 @@ export interface MapLayerHandler<TConfig = any> {
|
||||
*/
|
||||
update?: (data: PanelData) => void;
|
||||
|
||||
/** Optional callback to cleaup before getting removed */
|
||||
/** Optional callback for cleanup before getting removed */
|
||||
dispose?: () => void;
|
||||
|
||||
/** return react node for the legend */
|
||||
|
||||
@@ -38,7 +38,7 @@ export type PreferredVisualisationType = typeof preferredVisualizationTypes[numb
|
||||
export interface QueryResultMeta {
|
||||
type?: DataFrameType;
|
||||
|
||||
/** DatasSource Specific Values */
|
||||
/** DataSource Specific Values */
|
||||
custom?: Record<string, any>;
|
||||
|
||||
/** Stats */
|
||||
|
||||
@@ -87,6 +87,8 @@ export interface FeatureToggles {
|
||||
sessionRemoteCache?: boolean;
|
||||
disablePrometheusExemplarSampling?: boolean;
|
||||
alertingBacktesting?: boolean;
|
||||
editPanelCSVDragAndDrop?: boolean;
|
||||
alertingNoNormalState?: boolean;
|
||||
azureMultipleResourcePicker?: boolean;
|
||||
topNavCommandPalette?: boolean;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export type { MatcherConfig } from '@grafana/schema';
|
||||
import { MonoTypeOperatorFunction } from 'rxjs';
|
||||
|
||||
import { RegistryItemWithOptions } from '../utils/Registry';
|
||||
@@ -80,10 +81,6 @@ export interface ValueMatcherInfo<TOptions = any> extends RegistryItemWithOption
|
||||
isApplicable: (field: Field) => boolean;
|
||||
getDefaultOptions: (field: Field) => TOptions;
|
||||
}
|
||||
export interface MatcherConfig<TOptions = any> {
|
||||
id: string;
|
||||
options?: TOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
|
||||
@@ -8,6 +8,17 @@
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
|
||||
/**
|
||||
* TODO docs
|
||||
*/
|
||||
export interface DataSourceJsonData {
|
||||
alertmanagerUid?: string;
|
||||
authType?: string;
|
||||
defaultRegion?: string;
|
||||
manageAlerts?: boolean;
|
||||
profile?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* These are the common properties available to all queries in all datasources.
|
||||
* Specific implementations will *extend* this interface, adding the required
|
||||
@@ -40,6 +51,43 @@ export interface DataQuery {
|
||||
refId: string;
|
||||
}
|
||||
|
||||
export interface MapLayerOptions {
|
||||
/**
|
||||
* Custom options depending on the type
|
||||
*/
|
||||
config?: unknown;
|
||||
/**
|
||||
* Defines a frame MatcherConfig that may filter data for the given layer
|
||||
*/
|
||||
filterData?: unknown;
|
||||
/**
|
||||
* Common method to define geometry fields
|
||||
*/
|
||||
location?: FrameGeometrySource;
|
||||
/**
|
||||
* configured unique display name
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Common properties:
|
||||
* https://openlayers.org/en/latest/apidoc/module-ol_layer_Base-BaseLayer.html
|
||||
* Layer opacity (0-1)
|
||||
*/
|
||||
opacity?: number;
|
||||
/**
|
||||
* Check tooltip (defaults to true)
|
||||
*/
|
||||
tooltip?: boolean;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export enum FrameGeometrySourceMode {
|
||||
Auto = 'auto',
|
||||
Coords = 'coords',
|
||||
Geohash = 'geohash',
|
||||
Lookup = 'lookup',
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO docs
|
||||
*/
|
||||
@@ -487,6 +535,14 @@ export enum BarGaugeDisplayMode {
|
||||
Lcd = 'lcd',
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO docs
|
||||
*/
|
||||
export interface VizTooltipOptions {
|
||||
mode: TooltipDisplayMode;
|
||||
sort: SortOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internally, this is the "type" of cell that's being displayed
|
||||
* in the table such as colored text, JSON, gauge, etc.
|
||||
@@ -595,14 +651,24 @@ export interface DataSourceRef {
|
||||
uid?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO docs
|
||||
*/
|
||||
export interface VizTooltipOptions {
|
||||
mode: TooltipDisplayMode;
|
||||
sort: SortOrder;
|
||||
export interface FrameGeometrySource {
|
||||
/**
|
||||
* Path to Gazetteer
|
||||
*/
|
||||
gazetteer?: string;
|
||||
/**
|
||||
* Field mappings
|
||||
*/
|
||||
geohash?: string;
|
||||
latitude?: string;
|
||||
longitude?: string;
|
||||
lookup?: string;
|
||||
mode: FrameGeometrySourceMode;
|
||||
wkt?: string;
|
||||
}
|
||||
|
||||
export interface Labels {}
|
||||
|
||||
/**
|
||||
* Field options for each field within a table (e.g 10, "The String", 64.20, etc.)
|
||||
* Generally defines alignment, filtering capabilties, display options, etc.
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package common
|
||||
|
||||
// TODO docs
|
||||
DataSourceJsonData: {
|
||||
authType?: string
|
||||
defaultRegion?: string
|
||||
profile?: string
|
||||
manageAlerts?: bool
|
||||
alertmanagerUid?: string
|
||||
} @cuetsy(kind="interface")
|
||||
@@ -0,0 +1,33 @@
|
||||
package common
|
||||
|
||||
MapLayerOptions: {
|
||||
type: string
|
||||
// configured unique display name
|
||||
name: string
|
||||
// Custom options depending on the type
|
||||
config?: _
|
||||
// Common method to define geometry fields
|
||||
location?: FrameGeometrySource
|
||||
// Defines a frame MatcherConfig that may filter data for the given layer
|
||||
filterData?: _
|
||||
// Common properties:
|
||||
// https://openlayers.org/en/latest/apidoc/module-ol_layer_Base-BaseLayer.html
|
||||
// Layer opacity (0-1)
|
||||
opacity?: int64
|
||||
// Check tooltip (defaults to true)
|
||||
tooltip?: bool
|
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type")
|
||||
|
||||
FrameGeometrySourceMode: "auto" | "geohash" |"coords" | "lookup" @cuetsy(kind="enum",memberNames="Auto|Geohash|Coords|Lookup")
|
||||
|
||||
FrameGeometrySource: {
|
||||
mode: FrameGeometrySourceMode
|
||||
// Field mappings
|
||||
geohash?: string
|
||||
latitude?: string
|
||||
longitude?: string
|
||||
wkt?: string
|
||||
lookup?: string
|
||||
// Path to Gazetteer
|
||||
gazetteer?: string
|
||||
} @cuetsy(kind="interface")
|
||||
@@ -1,7 +1,5 @@
|
||||
package common
|
||||
|
||||
// TODO break this up into individual files. Current limitation on this is codegen logic, imports, dependencies
|
||||
|
||||
// TODO docs
|
||||
AxisPlacement: "auto" | "top" | "right" | "bottom" | "left" | "hidden" @cuetsy(kind="enum")
|
||||
|
||||
@@ -247,4 +245,6 @@ VizTooltipOptions: {
|
||||
sort: SortOrder
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
|
||||
Labels: {
|
||||
[string]: string
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
@@ -26,7 +26,6 @@ export type {
|
||||
SpecialValueMap,
|
||||
ValueMappingResult,
|
||||
Transformation,
|
||||
MatcherConfig,
|
||||
RowPanel,
|
||||
GraphPanel,
|
||||
HeatmapPanel
|
||||
@@ -46,7 +45,6 @@ export {
|
||||
SpecialValueMatch,
|
||||
DashboardCursorSync,
|
||||
defaultDashboardCursorSync,
|
||||
defaultMatcherConfig,
|
||||
defaultRowPanel
|
||||
} from './raw/dashboard/x/dashboard_types.gen';
|
||||
|
||||
@@ -65,6 +63,7 @@ export type {
|
||||
DataSourceRef,
|
||||
Panel,
|
||||
FieldConfigSource,
|
||||
MatcherConfig,
|
||||
FieldConfig
|
||||
} from './veneer/dashboard.types';
|
||||
|
||||
@@ -83,6 +82,7 @@ export {
|
||||
VariableHide,
|
||||
defaultPanel,
|
||||
defaultFieldConfigSource,
|
||||
defaultMatcherConfig,
|
||||
defaultFieldConfig
|
||||
} from './veneer/dashboard.types';
|
||||
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import * as raw from '../common/common.gen';
|
||||
|
||||
import { MatcherConfig } from './dashboard.types';
|
||||
|
||||
export interface MapLayerOptions<TConfig = any> extends raw.MapLayerOptions {
|
||||
// Custom options depending on the type
|
||||
config?: TConfig;
|
||||
filterData?: MatcherConfig;
|
||||
}
|
||||
|
||||
export interface DataQuery extends raw.DataQuery {
|
||||
// TODO remove explicit nulls
|
||||
datasource?: raw.DataSourceRef | null;
|
||||
|
||||
@@ -43,6 +43,10 @@ export interface FieldConfigSource<TOptions = Record<string, unknown>> extends r
|
||||
defaults: FieldConfig<TOptions>;
|
||||
}
|
||||
|
||||
export interface MatcherConfig<TConfig = any> extends raw.MatcherConfig {
|
||||
options?: TConfig;
|
||||
}
|
||||
|
||||
export const defaultDashboard = raw.defaultDashboard as Dashboard;
|
||||
export const defaultVariableModel = {
|
||||
...raw.defaultVariableModel,
|
||||
@@ -60,3 +64,4 @@ export const defaultVariableModel = {
|
||||
export const defaultPanel: Partial<Panel> = raw.defaultPanel;
|
||||
export const defaultFieldConfig: Partial<FieldConfig> = raw.defaultFieldConfig;
|
||||
export const defaultFieldConfigSource: Partial<FieldConfigSource> = raw.defaultFieldConfigSource;
|
||||
export const defaultMatcherConfig: Partial<MatcherConfig> = raw.defaultMatcherConfig;
|
||||
|
||||
@@ -146,14 +146,7 @@ Yes! grafana-toolkit supports TypeScript by default.
|
||||
|
||||
grafana-toolkit comes with Jest as a test runner.
|
||||
|
||||
Internally at Grafana we use Enzyme. If you are developing React plugin and you want to configure Enzyme as a testing utility, then you need to configure `enzyme-adapter-react`. To do so, create `<YOUR_PLUGIN_DIR>/config/jest-setup.ts` file that will provide necessary setup. Copy the following code into that file to get Enzyme working with React:
|
||||
|
||||
```ts
|
||||
import { configure } from 'enzyme';
|
||||
import Adapter from 'enzyme-adapter-react-16';
|
||||
|
||||
configure({ adapter: new Adapter() });
|
||||
```
|
||||
Internally at Grafana we use React Testing Library.
|
||||
|
||||
You can also set up Jest with shims of your needs by creating `jest-shim.ts` file in the same directory: `<YOUR_PLUGIN_DIR_>/config/jest-shim.ts`
|
||||
|
||||
|
||||
@@ -141,8 +141,6 @@
|
||||
"@testing-library/user-event": "14.4.3",
|
||||
"@types/common-tags": "^1.8.0",
|
||||
"@types/d3": "7.4.0",
|
||||
"@types/enzyme": "3.10.12",
|
||||
"@types/enzyme-adapter-react-16": "1.0.6",
|
||||
"@types/hoist-non-react-statics": "3.3.1",
|
||||
"@types/is-hotkey": "0.1.7",
|
||||
"@types/jest": "29.2.3",
|
||||
@@ -168,11 +166,9 @@
|
||||
"@types/testing-library__jest-dom": "5.14.5",
|
||||
"@types/tinycolor2": "1.4.3",
|
||||
"@types/uuid": "8.3.4",
|
||||
"@wojtekmaj/enzyme-adapter-react-17": "0.8.0",
|
||||
"common-tags": "1.8.2",
|
||||
"css-loader": "6.7.1",
|
||||
"csstype": "3.1.1",
|
||||
"enzyme": "3.11.0",
|
||||
"esbuild": "0.16.17",
|
||||
"expose-loader": "4.0.0",
|
||||
"mock-raf": "1.0.1",
|
||||
|
||||
@@ -311,3 +311,18 @@ export const clearButtonStyles = (theme: GrafanaTheme2) => {
|
||||
padding: 0;
|
||||
`;
|
||||
};
|
||||
|
||||
export const clearLinkButtonStyles = (theme: GrafanaTheme2) => {
|
||||
return css`
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
font-family: inherit;
|
||||
color: inherit;
|
||||
height: 100%;
|
||||
&:hover {
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
`;
|
||||
};
|
||||
|
||||
@@ -30,19 +30,23 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
|
||||
|
||||
return (
|
||||
<label className={cx(styles.wrapper, className)}>
|
||||
<input
|
||||
type="checkbox"
|
||||
className={styles.input}
|
||||
checked={value}
|
||||
disabled={disabled}
|
||||
onChange={handleOnChange}
|
||||
value={htmlValue}
|
||||
{...inputProps}
|
||||
ref={ref}
|
||||
/>
|
||||
<span className={styles.checkmark} />
|
||||
{label && <span className={styles.label}>{label}</span>}
|
||||
{description && <span className={styles.description}>{description}</span>}
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
className={styles.input}
|
||||
checked={value}
|
||||
disabled={disabled}
|
||||
onChange={handleOnChange}
|
||||
value={htmlValue}
|
||||
{...inputProps}
|
||||
ref={ref}
|
||||
/>
|
||||
<span className={styles.checkmark} />
|
||||
</div>
|
||||
<div>
|
||||
{label && <span className={styles.label}>{label}</span>}
|
||||
{description && <span className={styles.description}>{description}</span>}
|
||||
</div>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
@@ -55,6 +59,9 @@ export const getCheckboxStyles = stylesFactory((theme: GrafanaTheme2) => {
|
||||
|
||||
return {
|
||||
wrapper: css`
|
||||
display: flex;
|
||||
gap: ${theme.spacing(labelPadding)};
|
||||
align-items: baseline;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
font-size: 0;
|
||||
@@ -138,18 +145,17 @@ export const getCheckboxStyles = stylesFactory((theme: GrafanaTheme2) => {
|
||||
css`
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding-left: ${theme.spacing(labelPadding)};
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: -3px;
|
||||
max-width: fit-content;
|
||||
line-height: ${theme.typography.bodySmall.lineHeight};
|
||||
margin-bottom: 0;
|
||||
`
|
||||
),
|
||||
description: cx(
|
||||
labelStyles.description,
|
||||
css`
|
||||
line-height: ${theme.typography.bodySmall.lineHeight};
|
||||
padding-left: ${theme.spacing(checkboxSize + labelPadding)};
|
||||
margin-top: 0; /* The margin effectively comes from the top: -2px on the label above it */
|
||||
`
|
||||
),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { Editor } from 'slate-react';
|
||||
|
||||
import { createTheme } from '@grafana/data';
|
||||
|
||||
@@ -8,120 +7,102 @@ import { UnThemedQueryField } from './QueryField';
|
||||
|
||||
describe('<QueryField />', () => {
|
||||
it('should render with null initial value', () => {
|
||||
const wrapper = shallow(
|
||||
<UnThemedQueryField theme={createTheme()} query={null} onTypeahead={jest.fn()} portalOrigin="mock-origin" />
|
||||
);
|
||||
expect(wrapper.find('div').exists()).toBeTruthy();
|
||||
expect(() =>
|
||||
render(
|
||||
<UnThemedQueryField theme={createTheme()} query={null} onTypeahead={jest.fn()} portalOrigin="mock-origin" />
|
||||
)
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should render with empty initial value', () => {
|
||||
const wrapper = shallow(
|
||||
<UnThemedQueryField theme={createTheme()} query="" onTypeahead={jest.fn()} portalOrigin="mock-origin" />
|
||||
);
|
||||
expect(wrapper.find('div').exists()).toBeTruthy();
|
||||
expect(() =>
|
||||
render(<UnThemedQueryField theme={createTheme()} query="" onTypeahead={jest.fn()} portalOrigin="mock-origin" />)
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should render with initial value', () => {
|
||||
const wrapper = shallow(
|
||||
<UnThemedQueryField theme={createTheme()} query="my query" onTypeahead={jest.fn()} portalOrigin="mock-origin" />
|
||||
);
|
||||
expect(wrapper.find('div').exists()).toBeTruthy();
|
||||
expect(() =>
|
||||
render(
|
||||
<UnThemedQueryField theme={createTheme()} query="my query" onTypeahead={jest.fn()} portalOrigin="mock-origin" />
|
||||
)
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should execute query on blur', () => {
|
||||
const onRun = jest.fn();
|
||||
const wrapper = mount(
|
||||
<UnThemedQueryField
|
||||
theme={createTheme()}
|
||||
query="my query"
|
||||
onTypeahead={jest.fn()}
|
||||
onRunQuery={onRun}
|
||||
portalOrigin="mock-origin"
|
||||
/>
|
||||
);
|
||||
const field = wrapper.instance() as UnThemedQueryField;
|
||||
const ed = wrapper.find(Editor).instance() as Editor;
|
||||
expect(onRun.mock.calls.length).toBe(0);
|
||||
field.handleBlur(undefined, ed, () => {});
|
||||
expect(onRun.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should run onChange with clean text', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = shallow(
|
||||
<UnThemedQueryField
|
||||
theme={createTheme()}
|
||||
query={`my\r clean query `}
|
||||
onTypeahead={jest.fn()}
|
||||
onChange={onChange}
|
||||
portalOrigin="mock-origin"
|
||||
/>
|
||||
);
|
||||
const field = wrapper.instance() as UnThemedQueryField;
|
||||
field.runOnChange();
|
||||
expect(onChange.mock.calls.length).toBe(1);
|
||||
expect(onChange.mock.calls[0][0]).toBe('my clean query ');
|
||||
});
|
||||
|
||||
it('should run custom on blur, but not necessarily execute query', () => {
|
||||
const onBlur = jest.fn();
|
||||
const onRun = jest.fn();
|
||||
const wrapper = mount(
|
||||
<UnThemedQueryField
|
||||
theme={createTheme()}
|
||||
query="my query"
|
||||
onTypeahead={jest.fn()}
|
||||
onBlur={onBlur}
|
||||
onRunQuery={onRun}
|
||||
portalOrigin="mock-origin"
|
||||
/>
|
||||
);
|
||||
const field = wrapper.instance() as UnThemedQueryField;
|
||||
const ed = wrapper.find(Editor).instance() as Editor;
|
||||
expect(onBlur.mock.calls.length).toBe(0);
|
||||
expect(onRun.mock.calls.length).toBe(0);
|
||||
field.handleBlur(undefined, ed, () => {});
|
||||
expect(onBlur.mock.calls.length).toBe(1);
|
||||
expect(onRun.mock.calls.length).toBe(0);
|
||||
});
|
||||
describe('syntaxLoaded', () => {
|
||||
it('should re-render the editor after syntax has fully loaded', () => {
|
||||
const wrapper: any = shallow(
|
||||
<UnThemedQueryField theme={createTheme()} query="my query" portalOrigin="mock-origin" />
|
||||
const mockOnRichValueChange = jest.fn();
|
||||
const { rerender } = render(
|
||||
<UnThemedQueryField
|
||||
theme={createTheme()}
|
||||
query="my query"
|
||||
onRichValueChange={mockOnRichValueChange}
|
||||
portalOrigin="mock-origin"
|
||||
/>
|
||||
);
|
||||
const spyOnChange = jest.spyOn(wrapper.instance(), 'onChange').mockImplementation(jest.fn());
|
||||
wrapper.instance().editor = { insertText: () => ({ deleteBackward: () => ({ value: 'fooo' }) }) };
|
||||
wrapper.setProps({ syntaxLoaded: true });
|
||||
expect(spyOnChange).toHaveBeenCalledWith('fooo', true);
|
||||
rerender(
|
||||
<UnThemedQueryField
|
||||
theme={createTheme()}
|
||||
query="my query"
|
||||
syntaxLoaded
|
||||
onRichValueChange={mockOnRichValueChange}
|
||||
portalOrigin="mock-origin"
|
||||
/>
|
||||
);
|
||||
expect(mockOnRichValueChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not re-render the editor if syntax is already loaded', () => {
|
||||
const wrapper: any = shallow(
|
||||
<UnThemedQueryField theme={createTheme()} query="my query" portalOrigin="mock-origin" />
|
||||
const mockOnRichValueChange = jest.fn();
|
||||
const { rerender } = render(
|
||||
<UnThemedQueryField
|
||||
theme={createTheme()}
|
||||
query="my query"
|
||||
onRichValueChange={mockOnRichValueChange}
|
||||
syntaxLoaded
|
||||
portalOrigin="mock-origin"
|
||||
/>
|
||||
);
|
||||
const spyOnChange = jest.spyOn(wrapper.instance(), 'onChange').mockImplementation(jest.fn());
|
||||
wrapper.setProps({ syntaxLoaded: true });
|
||||
wrapper.instance().editor = {};
|
||||
wrapper.setProps({ syntaxLoaded: true });
|
||||
expect(spyOnChange).not.toBeCalled();
|
||||
});
|
||||
it('should not re-render the editor if editor itself is not defined', () => {
|
||||
const wrapper: any = shallow(
|
||||
<UnThemedQueryField theme={createTheme()} query="my query" portalOrigin="mock-origin" />
|
||||
rerender(
|
||||
<UnThemedQueryField
|
||||
theme={createTheme()}
|
||||
query="my query"
|
||||
onRichValueChange={mockOnRichValueChange}
|
||||
syntaxLoaded
|
||||
portalOrigin="mock-origin"
|
||||
/>
|
||||
);
|
||||
const spyOnChange = jest.spyOn(wrapper.instance(), 'onChange').mockImplementation(jest.fn());
|
||||
wrapper.setProps({ syntaxLoaded: true });
|
||||
expect(wrapper.instance().editor).toBeFalsy();
|
||||
expect(spyOnChange).not.toBeCalled();
|
||||
expect(mockOnRichValueChange).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('should not re-render the editor twice once syntax is fully loaded', () => {
|
||||
const wrapper: any = shallow(
|
||||
<UnThemedQueryField theme={createTheme()} query="my query" portalOrigin="mock-origin" />
|
||||
const mockOnRichValueChange = jest.fn();
|
||||
const { rerender } = render(
|
||||
<UnThemedQueryField
|
||||
theme={createTheme()}
|
||||
onRichValueChange={mockOnRichValueChange}
|
||||
query="my query"
|
||||
portalOrigin="mock-origin"
|
||||
/>
|
||||
);
|
||||
const spyOnChange = jest.spyOn(wrapper.instance(), 'onChange').mockImplementation(jest.fn());
|
||||
wrapper.instance().editor = { insertText: () => ({ deleteBackward: () => ({ value: 'fooo' }) }) };
|
||||
wrapper.setProps({ syntaxLoaded: true });
|
||||
wrapper.setProps({ syntaxLoaded: true });
|
||||
expect(spyOnChange).toBeCalledTimes(1);
|
||||
rerender(
|
||||
<UnThemedQueryField
|
||||
theme={createTheme()}
|
||||
syntaxLoaded
|
||||
onRichValueChange={mockOnRichValueChange}
|
||||
query="my query"
|
||||
portalOrigin="mock-origin"
|
||||
/>
|
||||
);
|
||||
rerender(
|
||||
<UnThemedQueryField
|
||||
theme={createTheme()}
|
||||
syntaxLoaded
|
||||
onRichValueChange={mockOnRichValueChange}
|
||||
query="my query"
|
||||
portalOrigin="mock-origin"
|
||||
/>
|
||||
);
|
||||
expect(mockOnRichValueChange).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,9 @@ import tinycolor from 'tinycolor2';
|
||||
import { DisplayValue, formattedValueToString } from '@grafana/data';
|
||||
import { TableCellBackgroundDisplayMode, TableCellOptions } from '@grafana/schema';
|
||||
|
||||
import { useStyles2 } from '../../themes';
|
||||
import { getCellLinks, getTextColorForAlphaBackground } from '../../utils';
|
||||
import { Button, clearLinkButtonStyles } from '../Button';
|
||||
import { DataLinksContextMenu } from '../DataLinks/DataLinksContextMenu';
|
||||
|
||||
import { CellActions } from './CellActions';
|
||||
@@ -31,6 +33,7 @@ export const DefaultCell: FC<TableCellProps> = (props) => {
|
||||
const cellOptions = getCellOptions(field);
|
||||
const cellStyle = getCellStyle(tableStyles, cellOptions, displayValue, inspectEnabled);
|
||||
const hasLinks = Boolean(getCellLinks(field, row)?.length);
|
||||
const clearButtonStyle = useStyles2(clearLinkButtonStyles);
|
||||
|
||||
return (
|
||||
<div {...cellProps} className={cellStyle}>
|
||||
@@ -39,11 +42,16 @@ export const DefaultCell: FC<TableCellProps> = (props) => {
|
||||
{hasLinks && (
|
||||
<DataLinksContextMenu links={() => getCellLinks(field, row) || []}>
|
||||
{(api) => {
|
||||
return (
|
||||
<div onClick={api.openMenu} className={getLinkStyle(tableStyles, cellOptions, api.targetClassName)}>
|
||||
{value}
|
||||
</div>
|
||||
);
|
||||
const content = <div className={getLinkStyle(tableStyles, cellOptions, api.targetClassName)}>{value}</div>;
|
||||
if (api.openMenu) {
|
||||
return (
|
||||
<Button className={cx(clearButtonStyle)} onClick={api.openMenu}>
|
||||
{content}
|
||||
</Button>
|
||||
);
|
||||
} else {
|
||||
return content;
|
||||
}
|
||||
}}
|
||||
</DataLinksContextMenu>
|
||||
)}
|
||||
|
||||
@@ -50,6 +50,8 @@ export const FilterPopup: FC<Props> = ({ column: { preFilteredRows, filterValue,
|
||||
|
||||
return (
|
||||
<ClickOutsideWrapper onClick={onCancel} useCapture={true}>
|
||||
{/* This is just blocking click events from bubbeling and should not have a keyboard interaction. */}
|
||||
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
|
||||
<div className={cx(styles.filterContainer)} onClick={stopPropagation}>
|
||||
<VerticalGroup spacing="lg">
|
||||
<VerticalGroup spacing="xs">
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { cx } from '@emotion/css';
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import { useStyles2 } from '../../themes';
|
||||
import { getCellLinks } from '../../utils';
|
||||
import { Button, clearLinkButtonStyles } from '../Button';
|
||||
import { DataLinksContextMenu } from '../DataLinks/DataLinksContextMenu';
|
||||
|
||||
import { TableCellProps } from './types';
|
||||
@@ -12,6 +14,7 @@ export const ImageCell: FC<TableCellProps> = (props) => {
|
||||
const displayValue = field.display!(cell.value);
|
||||
|
||||
const hasLinks = Boolean(getCellLinks(field, row)?.length);
|
||||
const clearButtonStyle = useStyles2(clearLinkButtonStyles);
|
||||
|
||||
return (
|
||||
<div {...cellProps} className={tableStyles.cellContainer}>
|
||||
@@ -19,11 +22,16 @@ export const ImageCell: FC<TableCellProps> = (props) => {
|
||||
{hasLinks && (
|
||||
<DataLinksContextMenu style={{ height: '100%' }} links={() => getCellLinks(field, row) || []}>
|
||||
{(api) => {
|
||||
return (
|
||||
<div onClick={api.openMenu} className={cx(tableStyles.imageCellLink, api.targetClassName)}>
|
||||
<img src={displayValue.text} className={tableStyles.imageCell} alt="" />
|
||||
</div>
|
||||
);
|
||||
const img = <img src={displayValue.text} className={tableStyles.imageCell} alt="" />;
|
||||
if (api.openMenu) {
|
||||
return (
|
||||
<Button className={cx(clearButtonStyle)} onClick={api.openMenu}>
|
||||
{img}
|
||||
</Button>
|
||||
);
|
||||
} else {
|
||||
return img;
|
||||
}
|
||||
}}
|
||||
</DataLinksContextMenu>
|
||||
)}
|
||||
|
||||
@@ -2,7 +2,9 @@ import { css, cx } from '@emotion/css';
|
||||
import { isString } from 'lodash';
|
||||
import React from 'react';
|
||||
|
||||
import { useStyles2 } from '../../themes';
|
||||
import { getCellLinks } from '../../utils';
|
||||
import { Button, clearLinkButtonStyles } from '../Button';
|
||||
import { DataLinksContextMenu } from '../DataLinks/DataLinksContextMenu';
|
||||
|
||||
import { CellActions } from './CellActions';
|
||||
@@ -28,6 +30,7 @@ export function JSONViewCell(props: TableCellProps): JSX.Element {
|
||||
}
|
||||
|
||||
const hasLinks = Boolean(getCellLinks(field, row)?.length);
|
||||
const clearButtonStyle = useStyles2(clearLinkButtonStyles);
|
||||
|
||||
return (
|
||||
<div {...cellProps} className={inspectEnabled ? tableStyles.cellContainerNoOverflow : tableStyles.cellContainer}>
|
||||
@@ -36,11 +39,15 @@ export function JSONViewCell(props: TableCellProps): JSX.Element {
|
||||
{hasLinks && (
|
||||
<DataLinksContextMenu links={() => getCellLinks(field, row) || []}>
|
||||
{(api) => {
|
||||
return (
|
||||
<div onClick={api.openMenu} className={api.targetClassName}>
|
||||
{displayValue}
|
||||
</div>
|
||||
);
|
||||
if (api.openMenu) {
|
||||
return (
|
||||
<Button className={cx(clearButtonStyle)} onClick={api.openMenu}>
|
||||
{displayValue}
|
||||
</Button>
|
||||
);
|
||||
} else {
|
||||
return <>{displayValue}</>;
|
||||
}
|
||||
}}
|
||||
</DataLinksContextMenu>
|
||||
)}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
"@testing-library/react": "12.1.4",
|
||||
"@testing-library/user-event": "14.4.3",
|
||||
"@types/deep-freeze": "^0.1.1",
|
||||
"@types/enzyme": "3.10.12",
|
||||
"@types/hoist-non-react-statics": "^3.3.1",
|
||||
"@types/jest": "29.2.3",
|
||||
"@types/lodash": "4.14.187",
|
||||
@@ -25,7 +24,6 @@
|
||||
"@types/slate-react": "0.22.9",
|
||||
"@types/testing-library__jest-dom": "5.14.5",
|
||||
"@types/tinycolor2": "1.4.3",
|
||||
"enzyme": "3.11.0",
|
||||
"sinon": "14.0.1",
|
||||
"typescript": "4.8.4"
|
||||
},
|
||||
|
||||
@@ -31,5 +31,5 @@ type FolderSearchHit struct {
|
||||
Uid string `json:"uid"`
|
||||
Title string `json:"title"`
|
||||
AccessControl accesscontrol.Metadata `json:"accessControl,omitempty"`
|
||||
ParentUID string `json:"parent_uid,omitempty"`
|
||||
ParentUID string `json:"parentUid,omitempty"`
|
||||
}
|
||||
|
||||
+2
-2
@@ -39,7 +39,7 @@ func (hs *HTTPServer) GetFolders(c *models.ReqContext) response.Response {
|
||||
OrgID: c.OrgID,
|
||||
Limit: c.QueryInt64("limit"),
|
||||
Page: c.QueryInt64("page"),
|
||||
UID: c.Query("parent_uid"),
|
||||
UID: c.Query("parentUid"),
|
||||
SignedInUser: c.SignedInUser,
|
||||
})
|
||||
} else {
|
||||
@@ -340,7 +340,7 @@ type GetFoldersParams struct {
|
||||
// The parent folder UID
|
||||
// in:query
|
||||
// required:false
|
||||
ParentUID string `json:"parent_uid"`
|
||||
ParentUID string `json:"parentUid"`
|
||||
}
|
||||
|
||||
// swagger:parameters getFolderByUID
|
||||
|
||||
+17
-17
@@ -13,24 +13,13 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/middleware/csrf"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/oauthtoken"
|
||||
"github.com/grafana/grafana/pkg/services/querylibrary"
|
||||
"github.com/grafana/grafana/pkg/services/searchV2"
|
||||
"github.com/grafana/grafana/pkg/services/stats"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity/httpentitystore"
|
||||
"github.com/grafana/grafana/pkg/services/store/k8saccess"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/avatar"
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
httpstatic "github.com/grafana/grafana/pkg/api/static"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/kvstore"
|
||||
@@ -41,7 +30,7 @@ import (
|
||||
loginpkg "github.com/grafana/grafana/pkg/login"
|
||||
"github.com/grafana/grafana/pkg/login/social"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/middleware/csrf"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/registry/corekind"
|
||||
@@ -49,6 +38,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
"github.com/grafana/grafana/pkg/services/annotations"
|
||||
"github.com/grafana/grafana/pkg/services/apikey"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/services/authn"
|
||||
"github.com/grafana/grafana/pkg/services/cleanup"
|
||||
"github.com/grafana/grafana/pkg/services/comments"
|
||||
"github.com/grafana/grafana/pkg/services/contexthandler"
|
||||
@@ -63,10 +54,12 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/encryption"
|
||||
"github.com/grafana/grafana/pkg/services/export"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/hooks"
|
||||
"github.com/grafana/grafana/pkg/services/ldap"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||
"github.com/grafana/grafana/pkg/services/librarypanels"
|
||||
"github.com/grafana/grafana/pkg/services/licensing"
|
||||
"github.com/grafana/grafana/pkg/services/live"
|
||||
"github.com/grafana/grafana/pkg/services/live/pushhttp"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
@@ -74,6 +67,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/navtree"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert"
|
||||
"github.com/grafana/grafana/pkg/services/notifications"
|
||||
"github.com/grafana/grafana/pkg/services/oauthtoken"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/playlist"
|
||||
"github.com/grafana/grafana/pkg/services/plugindashboards"
|
||||
@@ -83,9 +77,11 @@ import (
|
||||
publicdashboardsApi "github.com/grafana/grafana/pkg/services/publicdashboards/api"
|
||||
"github.com/grafana/grafana/pkg/services/query"
|
||||
"github.com/grafana/grafana/pkg/services/queryhistory"
|
||||
"github.com/grafana/grafana/pkg/services/querylibrary"
|
||||
"github.com/grafana/grafana/pkg/services/quota"
|
||||
"github.com/grafana/grafana/pkg/services/rendering"
|
||||
"github.com/grafana/grafana/pkg/services/search"
|
||||
"github.com/grafana/grafana/pkg/services/searchV2"
|
||||
"github.com/grafana/grafana/pkg/services/searchusers"
|
||||
"github.com/grafana/grafana/pkg/services/secrets"
|
||||
secretsKV "github.com/grafana/grafana/pkg/services/secrets/kvstore"
|
||||
@@ -94,7 +90,10 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/shorturls"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/star"
|
||||
"github.com/grafana/grafana/pkg/services/stats"
|
||||
"github.com/grafana/grafana/pkg/services/store"
|
||||
"github.com/grafana/grafana/pkg/services/store/entity/httpentitystore"
|
||||
"github.com/grafana/grafana/pkg/services/store/k8saccess"
|
||||
"github.com/grafana/grafana/pkg/services/tag"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
"github.com/grafana/grafana/pkg/services/teamguardian"
|
||||
@@ -102,6 +101,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/thumbs"
|
||||
"github.com/grafana/grafana/pkg/services/updatechecker"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/services/validations"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
@@ -130,10 +130,10 @@ type HTTPServer struct {
|
||||
RemoteCacheService *remotecache.RemoteCache
|
||||
ProvisioningService provisioning.ProvisioningService
|
||||
Login login.Service
|
||||
License models.Licensing
|
||||
License licensing.Licensing
|
||||
AccessControl accesscontrol.AccessControl
|
||||
DataProxy *datasourceproxy.DataSourceProxyService
|
||||
PluginRequestValidator models.PluginRequestValidator
|
||||
PluginRequestValidator validations.PluginRequestValidator
|
||||
pluginClient plugins.Client
|
||||
pluginStore plugins.Store
|
||||
pluginInstaller plugins.Installer
|
||||
@@ -222,9 +222,9 @@ type ServerOptions struct {
|
||||
}
|
||||
|
||||
func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routing.RouteRegister, bus bus.Bus,
|
||||
renderService rendering.Service, licensing models.Licensing, hooksService *hooks.HooksService,
|
||||
renderService rendering.Service, licensing licensing.Licensing, hooksService *hooks.HooksService,
|
||||
cacheService *localcache.CacheService, sqlStore *sqlstore.SQLStore, alertEngine *alerting.AlertEngine,
|
||||
pluginRequestValidator models.PluginRequestValidator, pluginStaticRouteResolver plugins.StaticRouteResolver,
|
||||
pluginRequestValidator validations.PluginRequestValidator, pluginStaticRouteResolver plugins.StaticRouteResolver,
|
||||
pluginDashboardService plugindashboards.Service, pluginStore plugins.Store, pluginClient plugins.Client,
|
||||
pluginErrorResolver plugins.ErrorResolver, pluginInstaller plugins.Installer, settingsProvider setting.Provider,
|
||||
dataSourceCache datasources.CacheService, userTokenService auth.UserTokenService,
|
||||
|
||||
+3
-2
@@ -15,6 +15,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
@@ -202,7 +203,7 @@ func (hs *HTTPServer) GetPluginSettingByID(c *models.ReqContext) response.Respon
|
||||
OrgID: c.OrgID,
|
||||
})
|
||||
if err != nil {
|
||||
if !errors.Is(err, models.ErrPluginSettingNotFound) {
|
||||
if !errors.Is(err, pluginsettings.ErrPluginSettingNotFound) {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to get plugin settings", nil)
|
||||
}
|
||||
} else {
|
||||
@@ -227,7 +228,7 @@ func (hs *HTTPServer) GetPluginSettingByID(c *models.ReqContext) response.Respon
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) UpdatePluginSetting(c *models.ReqContext) response.Response {
|
||||
cmd := models.UpdatePluginSettingCmd{}
|
||||
cmd := pluginsettings.UpdatePluginSettingCmd{}
|
||||
if err := web.Bind(c.Req, &cmd); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ package runner
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/server/backgroundsvcs"
|
||||
"github.com/grafana/grafana/pkg/server/usagestatssvcs"
|
||||
@@ -41,7 +40,7 @@ var wireExtsSet = wire.NewSet(
|
||||
wireSet,
|
||||
migrations.ProvideOSSMigrations,
|
||||
licensing.ProvideService,
|
||||
wire.Bind(new(models.Licensing), new(*licensing.OSSLicensingService)),
|
||||
wire.Bind(new(licensing.Licensing), new(*licensing.OSSLicensingService)),
|
||||
wire.Bind(new(registry.DatabaseMigrator), new(*migrations.OSSMigrations)),
|
||||
setting.ProvideProvider,
|
||||
wire.Bind(new(setting.Provider), new(*setting.OSSImpl)),
|
||||
@@ -56,7 +55,7 @@ var wireExtsSet = wire.NewSet(
|
||||
thumbs.ProvideCrawlerAuthSetupService,
|
||||
wire.Bind(new(thumbs.CrawlerAuthSetupService), new(*thumbs.OSSCrawlerAuthSetupService)),
|
||||
validations.ProvideValidator,
|
||||
wire.Bind(new(models.PluginRequestValidator), new(*validations.OSSPluginRequestValidator)),
|
||||
wire.Bind(new(validations.PluginRequestValidator), new(*validations.OSSPluginRequestValidator)),
|
||||
provisioning.ProvideService,
|
||||
wire.Bind(new(provisioning.ProvisioningService), new(*provisioning.ProvisioningServiceImpl)),
|
||||
backgroundsvcs.ProvideBackgroundServiceRegistry,
|
||||
|
||||
@@ -8,7 +8,7 @@ title: {{ .KindName }} kind
|
||||
|
||||
# {{ .KindName }} kind
|
||||
|
||||
### Maturity: {{ .KindMaturity }}
|
||||
### Version: {{ .KindVersion }}
|
||||
## Maturity: {{ .KindMaturity }}
|
||||
## Version: {{ .KindVersion }}
|
||||
|
||||
{{ .Markdown }}
|
||||
|
||||
@@ -169,7 +169,7 @@ func TestReduceExecute(t *testing.T) {
|
||||
|
||||
func randomReduceFunc() string {
|
||||
res := mathexp.GetSupportedReduceFuncs()
|
||||
return res[rand.Intn(len(res)-1)]
|
||||
return res[rand.Intn(len(res))]
|
||||
}
|
||||
|
||||
func TestResampleCommand_Execute(t *testing.T) {
|
||||
|
||||
@@ -4,14 +4,14 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
|
||||
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/validations"
|
||||
)
|
||||
|
||||
const HostRedirectValidationMiddlewareName = "host-redirect-validation"
|
||||
|
||||
func RedirectLimitMiddleware(reqValidator models.PluginRequestValidator) sdkhttpclient.Middleware {
|
||||
func RedirectLimitMiddleware(reqValidator validations.PluginRequestValidator) sdkhttpclient.Middleware {
|
||||
return sdkhttpclient.NamedMiddlewareFunc(HostRedirectValidationMiddlewareName, func(opts sdkhttpclient.Options, next http.RoundTripper) http.RoundTripper {
|
||||
return sdkhttpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
|
||||
res, err := next.RoundTrip(req)
|
||||
|
||||
@@ -6,19 +6,20 @@ import (
|
||||
"time"
|
||||
|
||||
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
"github.com/mwitkow/go-conntrack"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics/metricutil"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/validations"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/mwitkow/go-conntrack"
|
||||
)
|
||||
|
||||
var newProviderFunc = sdkhttpclient.NewProvider
|
||||
|
||||
// New creates a new HTTP client provider with pre-configured middlewares.
|
||||
func New(cfg *setting.Cfg, validator models.PluginRequestValidator, tracer tracing.Tracer) *sdkhttpclient.Provider {
|
||||
func New(cfg *setting.Cfg, validator validations.PluginRequestValidator, tracer tracing.Tracer) *sdkhttpclient.Provider {
|
||||
logger := log.New("httpclient")
|
||||
userAgent := fmt.Sprintf("Grafana/%s", cfg.BuildVersion)
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package kindsysreport
|
||||
|
||||
import (
|
||||
"cuelang.org/go/cue"
|
||||
)
|
||||
|
||||
type AttributeWalker struct {
|
||||
seen map[cue.Value]bool
|
||||
count map[string]int
|
||||
}
|
||||
|
||||
func (w *AttributeWalker) Count(sch cue.Value, attrs ...string) map[string]int {
|
||||
w.seen = make(map[cue.Value]bool)
|
||||
w.count = make(map[string]int)
|
||||
|
||||
for _, attr := range attrs {
|
||||
w.count[attr] = 0
|
||||
}
|
||||
|
||||
w.walk(cue.MakePath(), sch)
|
||||
return w.count
|
||||
}
|
||||
|
||||
func (w *AttributeWalker) walk(p cue.Path, v cue.Value) {
|
||||
if w.seen[v] {
|
||||
return
|
||||
}
|
||||
|
||||
w.seen[v] = true
|
||||
|
||||
for attr := range w.count {
|
||||
if found := v.Attribute(attr); found.Err() == nil {
|
||||
w.count[attr]++
|
||||
}
|
||||
}
|
||||
|
||||
// nolint: exhaustive
|
||||
switch v.Kind() {
|
||||
case cue.StructKind:
|
||||
// If current cue.Value is a reference to another
|
||||
// definition, we don't want to traverse its fields
|
||||
// individually, because we'll do so for the actual def.
|
||||
if v != cue.Dereference(v) {
|
||||
return
|
||||
}
|
||||
|
||||
iter, err := v.Fields(cue.All())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for iter.Next() {
|
||||
w.walk(appendPath(p, iter.Selector()), iter.Value())
|
||||
}
|
||||
if lv := v.LookupPath(cue.MakePath(cue.AnyString)); lv.Exists() {
|
||||
w.walk(appendPath(p, cue.AnyString), lv)
|
||||
}
|
||||
case cue.ListKind:
|
||||
list, err := v.List()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for i := 0; list.Next(); i++ {
|
||||
w.walk(appendPath(p, cue.Index(i)), list.Value())
|
||||
}
|
||||
if lv := v.LookupPath(cue.MakePath(cue.AnyIndex)); lv.Exists() {
|
||||
w.walk(appendPath(p, cue.AnyString), lv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func appendPath(p cue.Path, sel cue.Selector) cue.Path {
|
||||
return cue.MakePath(append(p.Selectors(), sel)...)
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package kindsysreport
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hmarr/codeowners"
|
||||
)
|
||||
|
||||
type CodeOwnersFinder struct {
|
||||
ruleset codeowners.Ruleset
|
||||
}
|
||||
|
||||
func NewCodeOwnersFinder(groot string) (CodeOwnersFinder, error) {
|
||||
//nolint:gosec
|
||||
file, err := os.Open(filepath.Join(groot, ".github", "CODEOWNERS"))
|
||||
if err != nil {
|
||||
return CodeOwnersFinder{}, err
|
||||
}
|
||||
|
||||
ruleset, err := codeowners.ParseFile(file)
|
||||
if err != nil {
|
||||
return CodeOwnersFinder{}, err
|
||||
}
|
||||
|
||||
return CodeOwnersFinder{
|
||||
ruleset: ruleset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f CodeOwnersFinder) FindFor(pp ...string) ([]string, error) {
|
||||
if len(f.ruleset) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Set, to avoid duplicates
|
||||
m := make(map[string]struct{})
|
||||
|
||||
for _, p := range pp {
|
||||
r, err := f.ruleset.Match(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// No rule found for path p
|
||||
if r == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, o := range r.Owners {
|
||||
m[o.Value] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
result = append(result, k)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
+197
-19
@@ -9,18 +9,41 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
|
||||
"github.com/grafana/codejen"
|
||||
"github.com/grafana/grafana/pkg/kindsys"
|
||||
"github.com/grafana/grafana/pkg/kindsys/kindsysreport"
|
||||
"github.com/grafana/grafana/pkg/plugins/pfs/corelist"
|
||||
"github.com/grafana/grafana/pkg/plugins/plugindef"
|
||||
"github.com/grafana/grafana/pkg/registry/corekind"
|
||||
"github.com/grafana/thema"
|
||||
)
|
||||
|
||||
const reportFileName = "report.json"
|
||||
const (
|
||||
// Program's output
|
||||
reportFileName = "report.json"
|
||||
|
||||
// External references
|
||||
repoBaseURL = "https://github.com/grafana/grafana/tree/main"
|
||||
docsBaseURL = "https://grafana.com/docs/grafana/next/developers/kinds"
|
||||
|
||||
// Local references
|
||||
coreTSPath = "packages/grafana-schema/src/raw/%s/%s/%s_types.gen.ts"
|
||||
coreGoPath = "pkg/kinds/%s"
|
||||
coreCUEPath = "kinds/%s/%s_kind.cue"
|
||||
|
||||
composableTSPath = "public/app/plugins/%s/%s/%s.gen.ts"
|
||||
composableGoPath = "pkg/tsdb/%s/kinds/%s/types_%s_gen.go"
|
||||
composableCUEPath = "public/app/plugins/%s/%s/%s.cue"
|
||||
)
|
||||
|
||||
func main() {
|
||||
report := buildKindStateReport()
|
||||
@@ -54,17 +77,67 @@ var plannedCoreKinds = []string{
|
||||
"QueryHistory",
|
||||
}
|
||||
|
||||
type KindStateReport struct {
|
||||
Kinds map[string]kindsys.SomeKindProperties `json:"kinds"`
|
||||
Dimensions map[string]Dimension `json:"dimensions"`
|
||||
type KindLinks struct {
|
||||
Schema string
|
||||
Go string
|
||||
Ts string
|
||||
Docs string
|
||||
}
|
||||
|
||||
func (r *KindStateReport) add(k kindsys.SomeKindProperties, category string) {
|
||||
type Kind struct {
|
||||
kindsys.SomeKindProperties
|
||||
Category string
|
||||
Links KindLinks
|
||||
GrafanaMaturityCount int
|
||||
CodeOwners []string
|
||||
}
|
||||
|
||||
// MarshalJSON is overwritten to marshal
|
||||
// kindsys.SomeKindProperties at root level.
|
||||
func (k Kind) MarshalJSON() ([]byte, error) {
|
||||
b, err := json.Marshal(k.SomeKindProperties)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var m map[string]interface{}
|
||||
if err = json.Unmarshal(b, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m["category"] = k.Category
|
||||
m["grafanaMaturityCount"] = k.GrafanaMaturityCount
|
||||
|
||||
if len(k.CodeOwners) == 0 {
|
||||
m["codeowners"] = []string{}
|
||||
} else {
|
||||
m["codeowners"] = k.CodeOwners
|
||||
}
|
||||
|
||||
m["links"] = map[string]string{}
|
||||
for _, ref := range []string{"Schema", "Go", "Ts", "Docs"} {
|
||||
refVal := reflect.ValueOf(k.Links).FieldByName(ref).String()
|
||||
if len(refVal) > 0 {
|
||||
m["links"].(map[string]string)[toCamelCase(ref)] = refVal
|
||||
} else {
|
||||
m["links"].(map[string]string)[toCamelCase(ref)] = "n/a"
|
||||
}
|
||||
}
|
||||
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
type KindStateReport struct {
|
||||
Kinds map[string]Kind `json:"kinds"`
|
||||
Dimensions map[string]Dimension `json:"dimensions"`
|
||||
}
|
||||
|
||||
func (r *KindStateReport) add(k Kind) {
|
||||
kName := k.Common().MachineName
|
||||
|
||||
r.Kinds[kName] = k
|
||||
r.Dimensions["maturity"][k.Common().Maturity.String()].add(kName)
|
||||
r.Dimensions["category"][category].add(kName)
|
||||
r.Dimensions["category"][k.Category].add(kName)
|
||||
}
|
||||
|
||||
type Dimension map[string]*DimensionValue
|
||||
@@ -85,7 +158,7 @@ func (dv *DimensionValue) add(s string) {
|
||||
// the final report.
|
||||
func emptyKindStateReport() *KindStateReport {
|
||||
return &KindStateReport{
|
||||
Kinds: make(map[string]kindsys.SomeKindProperties),
|
||||
Kinds: make(map[string]Kind),
|
||||
Dimensions: map[string]Dimension{
|
||||
"maturity": {
|
||||
"planned": emptyDimensionValue("planned"),
|
||||
@@ -114,12 +187,23 @@ func buildKindStateReport() *KindStateReport {
|
||||
r := emptyKindStateReport()
|
||||
b := corekind.NewBase(nil)
|
||||
|
||||
groot := filepath.Join(elsedie(os.Getwd())("cannot get cwd"), "..", "..")
|
||||
of := elsedie(kindsysreport.NewCodeOwnersFinder(groot))("cannot parse .github/codeowners")
|
||||
|
||||
seen := make(map[string]bool)
|
||||
for _, k := range b.All() {
|
||||
seen[k.Props().Common().Name] = true
|
||||
lin := k.Lineage()
|
||||
switch k.Props().(type) {
|
||||
case kindsys.CoreProperties:
|
||||
r.add(k.Props(), "core")
|
||||
links := buildCoreLinks(lin, k.Decl().Properties)
|
||||
r.add(Kind{
|
||||
SomeKindProperties: k.Props(),
|
||||
Category: "core",
|
||||
Links: links,
|
||||
GrafanaMaturityCount: grafanaMaturityAttrCount(lin.Latest().Underlying()),
|
||||
CodeOwners: findCodeOwners(of, links),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,25 +212,35 @@ func buildKindStateReport() *KindStateReport {
|
||||
continue
|
||||
}
|
||||
|
||||
r.add(kindsys.CoreProperties{
|
||||
CommonProperties: kindsys.CommonProperties{
|
||||
Name: kn,
|
||||
PluralName: kn + "s",
|
||||
MachineName: machinize(kn),
|
||||
PluralMachineName: machinize(kn) + "s",
|
||||
Maturity: "planned",
|
||||
r.add(Kind{
|
||||
SomeKindProperties: kindsys.CoreProperties{
|
||||
CommonProperties: kindsys.CommonProperties{
|
||||
Name: kn,
|
||||
PluralName: kn + "s",
|
||||
MachineName: machinize(kn),
|
||||
PluralMachineName: machinize(kn) + "s",
|
||||
Maturity: "planned",
|
||||
},
|
||||
},
|
||||
}, "core")
|
||||
Category: "core",
|
||||
})
|
||||
}
|
||||
|
||||
all := kindsys.SchemaInterfaces(nil)
|
||||
for _, pp := range corelist.New(nil) {
|
||||
for _, si := range all {
|
||||
if ck, has := pp.ComposableKinds[si.Name()]; has {
|
||||
r.add(ck.Props(), "composable")
|
||||
links := buildComposableLinks(pp.Properties, ck.Decl().Properties)
|
||||
r.add(Kind{
|
||||
SomeKindProperties: ck.Props(),
|
||||
Category: "composable",
|
||||
Links: links,
|
||||
GrafanaMaturityCount: grafanaMaturityAttrCount(ck.Lineage().Latest().Underlying()),
|
||||
CodeOwners: findCodeOwners(of, links),
|
||||
})
|
||||
} else if may := si.Should(string(pp.Properties.Type)); may {
|
||||
n := plugindef.DerivePascalName(pp.Properties) + si.Name()
|
||||
props := kindsys.ComposableProperties{
|
||||
ck := kindsys.ComposableProperties{
|
||||
SchemaInterface: si.Name(),
|
||||
CommonProperties: kindsys.CommonProperties{
|
||||
Name: n,
|
||||
@@ -157,7 +251,10 @@ func buildKindStateReport() *KindStateReport {
|
||||
Maturity: "planned",
|
||||
},
|
||||
}
|
||||
r.add(props, "composable")
|
||||
r.add(Kind{
|
||||
SomeKindProperties: ck,
|
||||
Category: "composable",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,6 +268,79 @@ func buildKindStateReport() *KindStateReport {
|
||||
return r
|
||||
}
|
||||
|
||||
func buildCoreLinks(lin thema.Lineage, cp kindsys.CoreProperties) KindLinks {
|
||||
const category = "core"
|
||||
vpath := fmt.Sprintf("v%v", lin.Latest().Version()[0])
|
||||
if cp.Maturity.Less(kindsys.MaturityStable) {
|
||||
vpath = "x"
|
||||
}
|
||||
|
||||
return KindLinks{
|
||||
Schema: elsedie(url.JoinPath(repoBaseURL, fmt.Sprintf(coreCUEPath, cp.MachineName, cp.MachineName)))("cannot build schema link"),
|
||||
Go: elsedie(url.JoinPath(repoBaseURL, fmt.Sprintf(coreGoPath, cp.MachineName)))("cannot build go link"),
|
||||
Ts: elsedie(url.JoinPath(repoBaseURL, fmt.Sprintf(coreTSPath, cp.MachineName, vpath, cp.MachineName)))("cannot build ts link"),
|
||||
Docs: elsedie(url.JoinPath(docsBaseURL, category, cp.MachineName, "schema-reference"))("cannot build docs link"),
|
||||
}
|
||||
}
|
||||
|
||||
// used to map names for those plugins that aren't following
|
||||
// naming conventions, like 'annonlist' which comes from "Annotations list".
|
||||
var irregularPluginNames = map[string]string{
|
||||
// Panel
|
||||
"alertgroups": "alertGroups",
|
||||
"annotationslist": "annolist",
|
||||
"dashboardlist": "dashlist",
|
||||
"nodegraph": "nodeGraph",
|
||||
"statetimeline": "state-timeline",
|
||||
"statushistory": "status-history",
|
||||
"tableold": "table-old",
|
||||
// Datasource
|
||||
"googlecloudmonitoring": "cloud-monitoring",
|
||||
"azuremonitor": "grafana-azure-monitor-datasource",
|
||||
"microsoftsqlserver": "mssql",
|
||||
"postgresql": "postgres",
|
||||
"testdatadb": "testdata",
|
||||
}
|
||||
|
||||
func buildComposableLinks(pp plugindef.PluginDef, cp kindsys.ComposableProperties) KindLinks {
|
||||
const category = "composable"
|
||||
schemaInterface := strings.ToLower(cp.SchemaInterface)
|
||||
|
||||
pName := strings.Replace(cp.MachineName, schemaInterface, "", 1)
|
||||
if irr, ok := irregularPluginNames[pName]; ok {
|
||||
pName = irr
|
||||
}
|
||||
|
||||
var goLink string
|
||||
if pp.Backend != nil && *pp.Backend {
|
||||
goLink = elsedie(url.JoinPath(repoBaseURL, fmt.Sprintf(composableGoPath, pName, schemaInterface, schemaInterface)))("cannot build go link")
|
||||
}
|
||||
|
||||
return KindLinks{
|
||||
Schema: elsedie(url.JoinPath(repoBaseURL, fmt.Sprintf(composableCUEPath, string(pp.Type), pName, schemaInterface)))("cannot build schema link"),
|
||||
Go: goLink,
|
||||
Ts: elsedie(url.JoinPath(repoBaseURL, fmt.Sprintf(composableTSPath, string(pp.Type), pName, schemaInterface)))("cannot build ts link"),
|
||||
Docs: elsedie(url.JoinPath(docsBaseURL, category, cp.MachineName, "schema-reference"))("cannot build docs link"),
|
||||
}
|
||||
}
|
||||
|
||||
func grafanaMaturityAttrCount(sch cue.Value) int {
|
||||
const attr = "grafanamaturity"
|
||||
aw := new(kindsysreport.AttributeWalker)
|
||||
return aw.Count(sch, attr)[attr]
|
||||
}
|
||||
|
||||
func findCodeOwners(of kindsysreport.CodeOwnersFinder, links KindLinks) []string {
|
||||
owners := elsedie(of.FindFor([]string{
|
||||
toLocalPath(links.Schema),
|
||||
toLocalPath(links.Go),
|
||||
toLocalPath(links.Ts),
|
||||
}...))("cannot find code owners")
|
||||
|
||||
sort.Strings(owners)
|
||||
return owners
|
||||
}
|
||||
|
||||
func machinize(s string) string {
|
||||
return strings.Map(func(r rune) rune {
|
||||
switch {
|
||||
@@ -190,6 +360,14 @@ func machinize(s string) string {
|
||||
}, s)
|
||||
}
|
||||
|
||||
func toCamelCase(s string) string {
|
||||
return strings.ToLower(string(s[0])) + s[1:]
|
||||
}
|
||||
|
||||
func toLocalPath(s string) string {
|
||||
return strings.Replace(s, repoBaseURL+"/", "", 1)
|
||||
}
|
||||
|
||||
type reportJenny struct{}
|
||||
|
||||
func (reportJenny) JennyName() string {
|
||||
|
||||
+1216
-513
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
package kindsys
|
||||
|
||||
// Ptr returns a pointer to a value of an arbitrary type.
|
||||
//
|
||||
// This function is provided to compensate for Grafana's Go code generation that
|
||||
// represents optional fields using pointers.
|
||||
//
|
||||
// Pointers are the only technically [correct, non-ambiguous] way of
|
||||
// representing an optional field in Go's type system. However, Go does not
|
||||
// allow taking the address of certain primitive types inline. That is,
|
||||
// this is invalid Go code:
|
||||
//
|
||||
// var str *string
|
||||
// str = &"colorless green ideas sleep furiously"
|
||||
//
|
||||
// This func allows making such declarations in a single line:
|
||||
//
|
||||
// var str *string
|
||||
// str = kindsys.Ptr("colorless green ideas sleep furiously")
|
||||
//
|
||||
// [correct, non-ambiguous]: https://github.com/grafana/grok/issues/1
|
||||
func Ptr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrPluginSettingNotFound = errors.New("plugin setting not found")
|
||||
)
|
||||
|
||||
type PluginSetting struct {
|
||||
Id int64
|
||||
PluginId string
|
||||
OrgId int64
|
||||
Enabled bool
|
||||
Pinned bool
|
||||
JsonData map[string]interface{}
|
||||
SecureJsonData map[string][]byte
|
||||
PluginVersion string
|
||||
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
}
|
||||
|
||||
type PluginSettingInfo struct {
|
||||
PluginID string `xorm:"plugin_id"`
|
||||
OrgID int64 `xorm:"org_id"`
|
||||
Enabled bool `xorm:"enabled"`
|
||||
Pinned bool `xorm:"pinned"`
|
||||
PluginVersion string `xorm:"plugin_id"`
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
// COMMANDS
|
||||
|
||||
// Also acts as api DTO
|
||||
type UpdatePluginSettingCmd struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Pinned bool `json:"pinned"`
|
||||
JsonData map[string]interface{} `json:"jsonData"`
|
||||
SecureJsonData map[string]string `json:"secureJsonData"`
|
||||
PluginVersion string `json:"version"`
|
||||
|
||||
PluginId string `json:"-"`
|
||||
OrgId int64 `json:"-"`
|
||||
EncryptedSecureJsonData map[string][]byte `json:"-"`
|
||||
}
|
||||
|
||||
// specific command, will only update version
|
||||
type UpdatePluginSettingVersionCmd struct {
|
||||
PluginVersion string
|
||||
PluginId string `json:"-"`
|
||||
OrgId int64 `json:"-"`
|
||||
}
|
||||
|
||||
// ---------------------
|
||||
// QUERIES
|
||||
|
||||
type GetPluginSettingByIdQuery struct {
|
||||
PluginId string
|
||||
OrgId int64
|
||||
Result *PluginSetting
|
||||
}
|
||||
|
||||
type PluginStateChangedEvent struct {
|
||||
PluginId string
|
||||
OrgId int64
|
||||
Enabled bool
|
||||
}
|
||||
@@ -64,6 +64,10 @@ func (cp *corePlugin) IsDecommissioned() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (cp *corePlugin) Target() backendplugin.Target {
|
||||
return backendplugin.TargetInMemory
|
||||
}
|
||||
|
||||
func (cp *corePlugin) CollectMetrics(_ context.Context, _ *backend.CollectMetricsRequest) (*backend.CollectMetricsResult, error) {
|
||||
return nil, backendplugin.ErrMethodNotImplemented
|
||||
}
|
||||
|
||||
@@ -120,6 +120,10 @@ func (p *grpcPlugin) IsDecommissioned() bool {
|
||||
return p.decommissioned
|
||||
}
|
||||
|
||||
func (p *grpcPlugin) Target() backendplugin.Target {
|
||||
return backendplugin.TargetLocal
|
||||
}
|
||||
|
||||
func (p *grpcPlugin) getPluginClient() (pluginClient, bool) {
|
||||
p.mutex.RLock()
|
||||
if p.client == nil || p.client.Exited() || p.pluginClient == nil {
|
||||
|
||||
@@ -17,9 +17,19 @@ type Plugin interface {
|
||||
Exited() bool
|
||||
Decommission() error
|
||||
IsDecommissioned() bool
|
||||
Target() Target
|
||||
backend.CollectMetricsHandler
|
||||
backend.CheckHealthHandler
|
||||
backend.QueryDataHandler
|
||||
backend.CallResourceHandler
|
||||
backend.StreamHandler
|
||||
}
|
||||
|
||||
type Target string
|
||||
|
||||
const (
|
||||
TargetNone Target = "none"
|
||||
TargetUnknown Target = "unknown"
|
||||
TargetInMemory Target = "in_memory"
|
||||
TargetLocal Target = "local"
|
||||
)
|
||||
|
||||
@@ -6,11 +6,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/plugins/config"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -18,20 +19,20 @@ var (
|
||||
Namespace: "grafana",
|
||||
Name: "plugin_request_total",
|
||||
Help: "The total amount of plugin requests",
|
||||
}, []string{"plugin_id", "endpoint", "status"})
|
||||
}, []string{"plugin_id", "endpoint", "status", "target"})
|
||||
|
||||
pluginRequestDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||
Namespace: "grafana",
|
||||
Name: "plugin_request_duration_milliseconds",
|
||||
Help: "Plugin request duration",
|
||||
Buckets: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10, 25, 50, 100},
|
||||
}, []string{"plugin_id", "endpoint"})
|
||||
}, []string{"plugin_id", "endpoint", "target"})
|
||||
)
|
||||
|
||||
var logger log.Logger = log.New("plugin.instrumentation")
|
||||
|
||||
// instrumentPluginRequest instruments success rate and latency of `fn`
|
||||
func instrumentPluginRequest(ctx context.Context, cfg *config.Cfg, pluginCtx *backend.PluginContext, endpoint string, fn func() error) error {
|
||||
func instrumentPluginRequest(ctx context.Context, cfg Cfg, pluginCtx *backend.PluginContext, endpoint string, fn func() error) error {
|
||||
status := "ok"
|
||||
|
||||
start := time.Now()
|
||||
@@ -44,8 +45,8 @@ func instrumentPluginRequest(ctx context.Context, cfg *config.Cfg, pluginCtx *ba
|
||||
}
|
||||
|
||||
elapsed := time.Since(start)
|
||||
pluginRequestDuration.WithLabelValues(pluginCtx.PluginID, endpoint).Observe(float64(elapsed / time.Millisecond))
|
||||
pluginRequestCounter.WithLabelValues(pluginCtx.PluginID, endpoint, status).Inc()
|
||||
pluginRequestDuration.WithLabelValues(pluginCtx.PluginID, endpoint, string(cfg.Target)).Observe(float64(elapsed / time.Millisecond))
|
||||
pluginRequestCounter.WithLabelValues(pluginCtx.PluginID, endpoint, status, string(cfg.Target)).Inc()
|
||||
|
||||
if cfg.LogDatasourceRequests {
|
||||
logParams := []interface{}{
|
||||
@@ -77,22 +78,27 @@ func instrumentPluginRequest(ctx context.Context, cfg *config.Cfg, pluginCtx *ba
|
||||
return err
|
||||
}
|
||||
|
||||
type Cfg struct {
|
||||
LogDatasourceRequests bool
|
||||
Target backendplugin.Target
|
||||
}
|
||||
|
||||
// InstrumentCollectMetrics instruments collectMetrics.
|
||||
func InstrumentCollectMetrics(ctx context.Context, req *backend.PluginContext, cfg *config.Cfg, fn func() error) error {
|
||||
func InstrumentCollectMetrics(ctx context.Context, req *backend.PluginContext, cfg Cfg, fn func() error) error {
|
||||
return instrumentPluginRequest(ctx, cfg, req, "collectMetrics", fn)
|
||||
}
|
||||
|
||||
// InstrumentCheckHealthRequest instruments checkHealth.
|
||||
func InstrumentCheckHealthRequest(ctx context.Context, req *backend.PluginContext, cfg *config.Cfg, fn func() error) error {
|
||||
func InstrumentCheckHealthRequest(ctx context.Context, req *backend.PluginContext, cfg Cfg, fn func() error) error {
|
||||
return instrumentPluginRequest(ctx, cfg, req, "checkHealth", fn)
|
||||
}
|
||||
|
||||
// InstrumentCallResourceRequest instruments callResource.
|
||||
func InstrumentCallResourceRequest(ctx context.Context, req *backend.PluginContext, cfg *config.Cfg, fn func() error) error {
|
||||
func InstrumentCallResourceRequest(ctx context.Context, req *backend.PluginContext, cfg Cfg, fn func() error) error {
|
||||
return instrumentPluginRequest(ctx, cfg, req, "callResource", fn)
|
||||
}
|
||||
|
||||
// InstrumentQueryDataRequest instruments success rate and latency of query data requests.
|
||||
func InstrumentQueryDataRequest(ctx context.Context, req *backend.PluginContext, cfg *config.Cfg, fn func() error) error {
|
||||
func InstrumentQueryDataRequest(ctx context.Context, req *backend.PluginContext, cfg Cfg, fn func() error) error {
|
||||
return instrumentPluginRequest(ctx, cfg, req, "queryData", fn)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package codegen
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/codejen"
|
||||
tsast "github.com/grafana/cuetsy/ts/ast"
|
||||
@@ -40,11 +41,10 @@ func (j *ptsJenny) Generate(decl *pfs.PluginDecl) (*codejen.File, error) {
|
||||
}
|
||||
}
|
||||
|
||||
slotname := decl.SchemaInterface.Name()
|
||||
v := decl.Lineage.Latest().Version()
|
||||
|
||||
tsf.Nodes = append(tsf.Nodes, tsast.Raw{
|
||||
Data: fmt.Sprintf("export const %sModelVersion = Object.freeze([%v, %v]);", slotname, v[0], v[1]),
|
||||
Data: fmt.Sprintf("export const %sModelVersion = Object.freeze([%v, %v]);", decl.SchemaInterface.Name(), v[0], v[1]),
|
||||
})
|
||||
|
||||
jf, err := j.inner.Generate(decl)
|
||||
@@ -56,7 +56,7 @@ func (j *ptsJenny) Generate(decl *pfs.PluginDecl) (*codejen.File, error) {
|
||||
Data: string(jf.Data),
|
||||
})
|
||||
|
||||
path := filepath.Join(j.root, decl.PluginPath, "models.gen.ts")
|
||||
path := filepath.Join(j.root, decl.PluginPath, fmt.Sprintf("%s.gen.ts", strings.ToLower(decl.SchemaInterface.Name())))
|
||||
data := []byte(tsf.String())
|
||||
data = data[:len(data)-1] // remove the additional line break added by the inner jenny
|
||||
|
||||
|
||||
@@ -3,16 +3,16 @@ package licensing
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/licensing"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
licensePath string
|
||||
license models.Licensing
|
||||
license licensing.Licensing
|
||||
}
|
||||
|
||||
func ProvideLicensing(cfg *setting.Cfg, l models.Licensing) *Service {
|
||||
func ProvideLicensing(cfg *setting.Cfg, l licensing.Licensing) *Service {
|
||||
return &Service{
|
||||
licensePath: cfg.EnterpriseLicensePath,
|
||||
license: l,
|
||||
@@ -21,7 +21,7 @@ func ProvideLicensing(cfg *setting.Cfg, l models.Licensing) *Service {
|
||||
|
||||
func (l Service) Environment() []string {
|
||||
var env []string
|
||||
if envProvider, ok := l.license.(models.LicenseEnvironment); ok {
|
||||
if envProvider, ok := l.license.(licensing.LicenseEnvironment); ok {
|
||||
for k, v := range envProvider.Environment() {
|
||||
env = append(env, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
|
||||
@@ -35,14 +35,17 @@ func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest)
|
||||
return nil, fmt.Errorf("req cannot be nil")
|
||||
}
|
||||
|
||||
plugin, exists := s.plugin(ctx, req.PluginContext.PluginID)
|
||||
p, exists := s.plugin(ctx, req.PluginContext.PluginID)
|
||||
if !exists {
|
||||
return nil, plugins.ErrPluginNotRegistered.Errorf("%w", backendplugin.ErrPluginNotRegistered)
|
||||
}
|
||||
|
||||
var resp *backend.QueryDataResponse
|
||||
err := instrumentation.InstrumentQueryDataRequest(ctx, &req.PluginContext, s.cfg, func() (innerErr error) {
|
||||
resp, innerErr = plugin.QueryData(ctx, req)
|
||||
err := instrumentation.InstrumentQueryDataRequest(ctx, &req.PluginContext, instrumentation.Cfg{
|
||||
LogDatasourceRequests: s.cfg.LogDatasourceRequests,
|
||||
Target: p.Target(),
|
||||
}, func() (innerErr error) {
|
||||
resp, innerErr = p.QueryData(ctx, req)
|
||||
return
|
||||
})
|
||||
|
||||
@@ -83,7 +86,10 @@ func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceReq
|
||||
if !exists {
|
||||
return backendplugin.ErrPluginNotRegistered
|
||||
}
|
||||
err := instrumentation.InstrumentCallResourceRequest(ctx, &req.PluginContext, s.cfg, func() error {
|
||||
err := instrumentation.InstrumentCallResourceRequest(ctx, &req.PluginContext, instrumentation.Cfg{
|
||||
LogDatasourceRequests: s.cfg.LogDatasourceRequests,
|
||||
Target: p.Target(),
|
||||
}, func() error {
|
||||
removeConnectionHeaders(req.Headers)
|
||||
removeHopByHopHeaders(req.Headers)
|
||||
|
||||
@@ -120,7 +126,10 @@ func (s *Service) CollectMetrics(ctx context.Context, req *backend.CollectMetric
|
||||
}
|
||||
|
||||
var resp *backend.CollectMetricsResult
|
||||
err := instrumentation.InstrumentCollectMetrics(ctx, &req.PluginContext, s.cfg, func() (innerErr error) {
|
||||
err := instrumentation.InstrumentCollectMetrics(ctx, &req.PluginContext, instrumentation.Cfg{
|
||||
LogDatasourceRequests: s.cfg.LogDatasourceRequests,
|
||||
Target: p.Target(),
|
||||
}, func() (innerErr error) {
|
||||
resp, innerErr = p.CollectMetrics(ctx, req)
|
||||
return
|
||||
})
|
||||
@@ -142,7 +151,10 @@ func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthReque
|
||||
}
|
||||
|
||||
var resp *backend.CheckHealthResult
|
||||
err := instrumentation.InstrumentCheckHealthRequest(ctx, &req.PluginContext, s.cfg, func() (innerErr error) {
|
||||
err := instrumentation.InstrumentCheckHealthRequest(ctx, &req.PluginContext, instrumentation.Cfg{
|
||||
LogDatasourceRequests: s.cfg.LogDatasourceRequests,
|
||||
Target: p.Target(),
|
||||
}, func() (innerErr error) {
|
||||
resp, innerErr = p.CheckHealth(ctx, req)
|
||||
return
|
||||
})
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/adapters"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
@@ -80,9 +79,9 @@ func (p *Provider) pluginContext(ctx context.Context, pluginID string, user *use
|
||||
|
||||
ps, err := p.getCachedPluginSettings(ctx, pluginID, user)
|
||||
if err != nil {
|
||||
// models.ErrPluginSettingNotFound is expected if there's no row found for plugin setting in database (if non-app plugin).
|
||||
// pluginsettings.ErrPluginSettingNotFound is expected if there's no row found for plugin setting in database (if non-app plugin).
|
||||
// If it's not this expected error something is wrong with cache or database and we return the error to the client.
|
||||
if !errors.Is(err, models.ErrPluginSettingNotFound) {
|
||||
if !errors.Is(err, pluginsettings.ErrPluginSettingNotFound) {
|
||||
return backend.PluginContext{}, false, fmt.Errorf("%v: %w", "Failed to get plugin settings", err)
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -267,6 +267,16 @@ func (p *Plugin) Exited() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Plugin) Target() backendplugin.Target {
|
||||
if !p.Backend {
|
||||
return backendplugin.TargetNone
|
||||
}
|
||||
if p.client == nil {
|
||||
return backendplugin.TargetUnknown
|
||||
}
|
||||
return p.client.Target()
|
||||
}
|
||||
|
||||
func (p *Plugin) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
pluginClient, ok := p.Client()
|
||||
if !ok {
|
||||
|
||||
@@ -6,7 +6,6 @@ package server
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/server/backgroundsvcs"
|
||||
@@ -44,7 +43,7 @@ var wireExtsBasicSet = wire.NewSet(
|
||||
wire.Bind(new(auth.UserTokenService), new(*authimpl.UserAuthTokenService)),
|
||||
wire.Bind(new(auth.UserTokenBackgroundService), new(*authimpl.UserAuthTokenService)),
|
||||
licensing.ProvideService,
|
||||
wire.Bind(new(models.Licensing), new(*licensing.OSSLicensingService)),
|
||||
wire.Bind(new(licensing.Licensing), new(*licensing.OSSLicensingService)),
|
||||
setting.ProvideProvider,
|
||||
wire.Bind(new(setting.Provider), new(*setting.OSSImpl)),
|
||||
acimpl.ProvideService,
|
||||
@@ -54,7 +53,7 @@ var wireExtsBasicSet = wire.NewSet(
|
||||
thumbs.ProvideCrawlerAuthSetupService,
|
||||
wire.Bind(new(thumbs.CrawlerAuthSetupService), new(*thumbs.OSSCrawlerAuthSetupService)),
|
||||
validations.ProvideValidator,
|
||||
wire.Bind(new(models.PluginRequestValidator), new(*validations.OSSPluginRequestValidator)),
|
||||
wire.Bind(new(validations.PluginRequestValidator), new(*validations.OSSPluginRequestValidator)),
|
||||
provisioning.ProvideService,
|
||||
wire.Bind(new(provisioning.ProvisioningService), new(*provisioning.ProvisioningServiceImpl)),
|
||||
backgroundsvcs.ProvideBackgroundServiceRegistry,
|
||||
|
||||
@@ -11,13 +11,13 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/models/roletype"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/database"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/licensing"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
@@ -278,7 +278,7 @@ func TestService_DeclarePluginRoles(t *testing.T) {
|
||||
func TestService_RegisterFixedRoles(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
token models.Licensing
|
||||
token licensing.Licensing
|
||||
registrations []accesscontrol.RoleRegistration
|
||||
wantErr bool
|
||||
}{
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/licensing"
|
||||
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
||||
"github.com/grafana/grafana/pkg/services/serviceaccounts/retriever"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
@@ -40,7 +41,7 @@ var (
|
||||
|
||||
func ProvideTeamPermissions(
|
||||
cfg *setting.Cfg, router routing.RouteRegister, sql db.DB,
|
||||
ac accesscontrol.AccessControl, license models.Licensing, service accesscontrol.Service,
|
||||
ac accesscontrol.AccessControl, license licensing.Licensing, service accesscontrol.Service,
|
||||
teamService team.Service, userService user.Service,
|
||||
) (*TeamPermissionsService, error) {
|
||||
options := resourcepermissions.Options{
|
||||
@@ -114,7 +115,7 @@ var DashboardAdminActions = append(DashboardEditActions, []string{dashboards.Act
|
||||
|
||||
func ProvideDashboardPermissions(
|
||||
cfg *setting.Cfg, router routing.RouteRegister, sql db.DB, ac accesscontrol.AccessControl,
|
||||
license models.Licensing, dashboardStore dashboards.Store, service accesscontrol.Service,
|
||||
license licensing.Licensing, dashboardStore dashboards.Store, service accesscontrol.Service,
|
||||
teamService team.Service, userService user.Service,
|
||||
) (*DashboardPermissionsService, error) {
|
||||
getDashboard := func(ctx context.Context, orgID int64, resourceID string) (*dashboards.Dashboard, error) {
|
||||
@@ -193,7 +194,7 @@ var FolderAdminActions = append(FolderEditActions, []string{dashboards.ActionFol
|
||||
|
||||
func ProvideFolderPermissions(
|
||||
cfg *setting.Cfg, router routing.RouteRegister, sql db.DB, accesscontrol accesscontrol.AccessControl,
|
||||
license models.Licensing, dashboardStore dashboards.Store, service accesscontrol.Service,
|
||||
license licensing.Licensing, dashboardStore dashboards.Store, service accesscontrol.Service,
|
||||
teamService team.Service, userService user.Service,
|
||||
) (*FolderPermissionsService, error) {
|
||||
options := resourcepermissions.Options{
|
||||
@@ -284,7 +285,7 @@ type ServiceAccountPermissionsService struct {
|
||||
|
||||
func ProvideServiceAccountPermissions(
|
||||
cfg *setting.Cfg, router routing.RouteRegister, sql db.DB, ac accesscontrol.AccessControl,
|
||||
license models.Licensing, serviceAccountRetrieverService *retriever.Service, service accesscontrol.Service,
|
||||
license licensing.Licensing, serviceAccountRetrieverService *retriever.Service, service accesscontrol.Service,
|
||||
teamService team.Service, userService user.Service,
|
||||
) (*ServiceAccountPermissionsService, error) {
|
||||
options := resourcepermissions.Options{
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/licensing"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
@@ -49,7 +49,7 @@ type Store interface {
|
||||
}
|
||||
|
||||
func New(
|
||||
options Options, cfg *setting.Cfg, router routing.RouteRegister, license models.Licensing,
|
||||
options Options, cfg *setting.Cfg, router routing.RouteRegister, license licensing.Licensing,
|
||||
ac accesscontrol.AccessControl, service accesscontrol.Service, sqlStore db.DB,
|
||||
teamService team.Service, userService user.Service,
|
||||
) (*Service, error) {
|
||||
@@ -104,7 +104,7 @@ type Service struct {
|
||||
service accesscontrol.Service
|
||||
store Store
|
||||
api *api
|
||||
license models.Licensing
|
||||
license licensing.Licensing
|
||||
|
||||
options Options
|
||||
permissions []string
|
||||
|
||||
@@ -15,13 +15,13 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/annotations"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/encryption"
|
||||
"github.com/grafana/grafana/pkg/services/notifications"
|
||||
"github.com/grafana/grafana/pkg/services/rendering"
|
||||
"github.com/grafana/grafana/pkg/services/validations"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||
"github.com/grafana/grafana/pkg/util/ticker"
|
||||
@@ -32,7 +32,7 @@ import (
|
||||
// are sent.
|
||||
type AlertEngine struct {
|
||||
RenderService rendering.Service
|
||||
RequestValidator models.PluginRequestValidator
|
||||
RequestValidator validations.PluginRequestValidator
|
||||
DataService legacydata.RequestHandler
|
||||
Cfg *setting.Cfg
|
||||
|
||||
@@ -58,7 +58,7 @@ func (e *AlertEngine) IsDisabled() bool {
|
||||
}
|
||||
|
||||
// ProvideAlertEngine returns a new AlertEngine.
|
||||
func ProvideAlertEngine(renderer rendering.Service, requestValidator models.PluginRequestValidator,
|
||||
func ProvideAlertEngine(renderer rendering.Service, requestValidator validations.PluginRequestValidator,
|
||||
dataService legacydata.RequestHandler, usageStatsService usagestats.Service, encryptionService encryption.Internal,
|
||||
notificationService *notifications.NotificationService, tracer tracing.Tracer, store AlertStore, cfg *setting.Cfg,
|
||||
dashAlertExtractor DashAlertExtractor, dashboardService dashboards.DashboardService, cacheService *localcache.CacheService, dsService datasources.DataSourceService, annotationsRepo annotations.Repository) *AlertEngine {
|
||||
|
||||
@@ -7,11 +7,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
alertmodels "github.com/grafana/grafana/pkg/services/alerting/models"
|
||||
"github.com/grafana/grafana/pkg/services/annotations"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/validations"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
@@ -37,7 +37,7 @@ type EvalContext struct {
|
||||
NoDataFound bool
|
||||
PrevAlertState alertmodels.AlertStateType
|
||||
|
||||
RequestValidator models.PluginRequestValidator
|
||||
RequestValidator validations.PluginRequestValidator
|
||||
|
||||
Ctx context.Context
|
||||
|
||||
@@ -48,7 +48,7 @@ type EvalContext struct {
|
||||
}
|
||||
|
||||
// NewEvalContext is the EvalContext constructor.
|
||||
func NewEvalContext(alertCtx context.Context, rule *Rule, requestValidator models.PluginRequestValidator,
|
||||
func NewEvalContext(alertCtx context.Context, rule *Rule, requestValidator validations.PluginRequestValidator,
|
||||
alertStore AlertStore, dashboardService dashboards.DashboardService, dsService datasources.DataSourceService, annotationRepo annotations.Repository) *EvalContext {
|
||||
return &EvalContext{
|
||||
Ctx: alertCtx,
|
||||
|
||||
@@ -17,12 +17,13 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/oauthtoken"
|
||||
"github.com/grafana/grafana/pkg/services/secrets"
|
||||
"github.com/grafana/grafana/pkg/services/validations"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
||||
func ProvideService(dataSourceCache datasources.CacheService, plugReqValidator models.PluginRequestValidator,
|
||||
func ProvideService(dataSourceCache datasources.CacheService, plugReqValidator validations.PluginRequestValidator,
|
||||
pluginStore plugins.Store, cfg *setting.Cfg, httpClientProvider httpclient.Provider,
|
||||
oauthTokenService *oauthtoken.Service, dsService datasources.DataSourceService,
|
||||
tracer tracing.Tracer, secretsService secrets.Service) *DataSourceProxyService {
|
||||
@@ -41,7 +42,7 @@ func ProvideService(dataSourceCache datasources.CacheService, plugReqValidator m
|
||||
|
||||
type DataSourceProxyService struct {
|
||||
DataSourceCache datasources.CacheService
|
||||
PluginRequestValidator models.PluginRequestValidator
|
||||
PluginRequestValidator validations.PluginRequestValidator
|
||||
pluginStore plugins.Store
|
||||
Cfg *setting.Cfg
|
||||
HTTPClientProvider httpclient.Provider
|
||||
@@ -131,7 +132,7 @@ func (p *DataSourceProxyService) proxyDatasourceRequest(c *models.ReqContext, ds
|
||||
proxy.HandleRequest()
|
||||
}
|
||||
|
||||
var proxyPathRegexp = regexp.MustCompile(`^\/api\/datasources\/proxy\/([\d]+|uid\/[\w]+)\/?`)
|
||||
var proxyPathRegexp = regexp.MustCompile(`^\/api\/datasources\/proxy\/([\d]+|uid\/[\w-]+)\/?`)
|
||||
|
||||
func extractProxyPath(originalRawPath string) string {
|
||||
return proxyPathRegexp.ReplaceAllString(originalRawPath, "")
|
||||
|
||||
@@ -32,6 +32,14 @@ func TestDataProxy(t *testing.T) {
|
||||
"/api/datasources/proxy/uid/26MI0wZ7k/some/thing",
|
||||
"some/thing",
|
||||
},
|
||||
{
|
||||
"/api/datasources/proxy/uid/pUWo-no4k/search",
|
||||
"search",
|
||||
},
|
||||
{
|
||||
"/api/datasources/proxy/uid/pUWo_no4k/search",
|
||||
"search",
|
||||
},
|
||||
{
|
||||
"/api/datasources/proxy/uid/26MI0wZ7k/api/services/afsd%2Fafsd/operations",
|
||||
"api/services/afsd%2Fafsd/operations",
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/licensing"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -18,7 +18,7 @@ var (
|
||||
|
||||
type FeatureManager struct {
|
||||
isDevMod bool
|
||||
licensing models.Licensing
|
||||
licensing licensing.Licensing
|
||||
flags map[string]*FeatureFlag
|
||||
enabled map[string]bool // only the "on" values
|
||||
config string // path to config file
|
||||
|
||||
@@ -397,6 +397,12 @@ var (
|
||||
Description: "Rule backtesting API for alerting",
|
||||
State: FeatureStateAlpha,
|
||||
},
|
||||
{
|
||||
Name: "editPanelCSVDragAndDrop",
|
||||
Description: "Enables drag and drop for CSV and Excel files",
|
||||
FrontendOnly: true,
|
||||
State: FeatureStateAlpha,
|
||||
},
|
||||
{
|
||||
Name: "alertingNoNormalState",
|
||||
Description: "Stop maintaining state of alerts that are not firing",
|
||||
@@ -408,5 +414,11 @@ var (
|
||||
Description: "Azure multiple resource picker",
|
||||
State: FeatureStateAlpha,
|
||||
},
|
||||
{
|
||||
Name: "topNavCommandPalette",
|
||||
Description: "Launch the Command Palette from the top navigation search box",
|
||||
State: FeatureStateBeta,
|
||||
FrontendOnly: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -5,12 +5,12 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/licensing"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -22,7 +22,7 @@ var (
|
||||
}, []string{"name"})
|
||||
)
|
||||
|
||||
func ProvideManagerService(cfg *setting.Cfg, licensing models.Licensing) (*FeatureManager, error) {
|
||||
func ProvideManagerService(cfg *setting.Cfg, licensing licensing.Licensing) (*FeatureManager, error) {
|
||||
mgmt := &FeatureManager{
|
||||
isDevMod: setting.Env != setting.Prod,
|
||||
licensing: licensing,
|
||||
|
||||
@@ -3,9 +3,10 @@ package featuremgmt
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/licensing"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func TestFeatureService(t *testing.T) {
|
||||
@@ -47,7 +48,7 @@ func TestFeatureService(t *testing.T) {
|
||||
}
|
||||
|
||||
var (
|
||||
_ models.Licensing = (*stubLicenseServier)(nil)
|
||||
_ licensing.Licensing = (*stubLicenseServier)(nil)
|
||||
)
|
||||
|
||||
type stubLicenseServier struct {
|
||||
|
||||
@@ -291,6 +291,10 @@ const (
|
||||
// Rule backtesting API for alerting
|
||||
FlagAlertingBacktesting = "alertingBacktesting"
|
||||
|
||||
// FlagEditPanelCSVDragAndDrop
|
||||
// Enables drag and drop for CSV and Excel files
|
||||
FlagEditPanelCSVDragAndDrop = "editPanelCSVDragAndDrop"
|
||||
|
||||
// FlagAlertingNoNormalState
|
||||
// Stop maintaining state of alerts that are not firing
|
||||
FlagAlertingNoNormalState = "alertingNoNormalState"
|
||||
@@ -298,4 +302,8 @@ const (
|
||||
// FlagAzureMultipleResourcePicker
|
||||
// Azure multiple resource picker
|
||||
FlagAzureMultipleResourcePicker = "azureMultipleResourcePicker"
|
||||
|
||||
// FlagTopNavCommandPalette
|
||||
// Launch the Command Palette from the top navigation search box
|
||||
FlagTopNavCommandPalette = "topNavCommandPalette"
|
||||
)
|
||||
|
||||
@@ -3,10 +3,10 @@ package licensingtest
|
||||
import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/licensing"
|
||||
)
|
||||
|
||||
var _ models.Licensing = new(FakeLicensing)
|
||||
var _ licensing.Licensing = new(FakeLicensing)
|
||||
|
||||
func NewFakeLicensing() *FakeLicensing {
|
||||
return &FakeLicensing{&mock.Mock{}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package models
|
||||
package licensing
|
||||
|
||||
type Licensing interface {
|
||||
// Expiry returns the unix epoch timestamp when the license expires, or 0 if no valid license is provided
|
||||
@@ -218,14 +218,6 @@ func (s *ServiceImpl) getHomeNode(c *models.ReqContext, prefs *pref.Preference)
|
||||
homeUrl = homePage
|
||||
}
|
||||
|
||||
if prefs.HomeDashboardID != 0 {
|
||||
slugQuery := dashboards.GetDashboardRefByIDQuery{ID: prefs.HomeDashboardID}
|
||||
err := s.dashboardService.GetDashboardUIDByID(c.Req.Context(), &slugQuery)
|
||||
if err == nil {
|
||||
homeUrl = dashboards.GetDashboardURL(slugQuery.Result.UID, slugQuery.Result.Slug)
|
||||
}
|
||||
}
|
||||
|
||||
homeNode := &navtree.NavLink{
|
||||
Text: "Home",
|
||||
Id: "home",
|
||||
@@ -279,7 +271,7 @@ func (s *ServiceImpl) getProfileNode(c *models.ReqContext) *navtree.NavLink {
|
||||
|
||||
children := []*navtree.NavLink{
|
||||
{
|
||||
Text: "Preferences", Id: "profile/settings", Url: s.cfg.AppSubURL + "/profile", Icon: "sliders-v-alt",
|
||||
Text: "Profile", Id: "profile/settings", Url: s.cfg.AppSubURL + "/profile", Icon: "sliders-v-alt",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -69,8 +69,8 @@ func validRule() apimodels.PostableExtendedRuleNode {
|
||||
},
|
||||
},
|
||||
UID: util.GenerateShortUID(),
|
||||
NoDataState: allNoData[rand.Intn(len(allNoData)-1)],
|
||||
ExecErrState: allExecError[rand.Intn(len(allExecError)-1)],
|
||||
NoDataState: allNoData[rand.Intn(len(allNoData))],
|
||||
ExecErrState: allExecError[rand.Intn(len(allExecError))],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user