mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Prometheus schematization (#63878)
* Schematize prometheus * revert changes * close response body * Update report.json * Update pkg/tsdb/prometheus/models/query.go Co-authored-by: sam boyer <sdboyer@grafana.com> * Use without pointers * remove unused * Specify query format * Rename * Clean up schema * Update public/app/plugins/datasource/prometheus/dataquery.cue Co-authored-by: Ryan McKinley <ryantxu@gmail.com> * Update pkg/tsdb/prometheus/models/query.go Co-authored-by: Ryan McKinley <ryantxu@gmail.com> * Clean up tests * Update public/app/plugins/datasource/prometheus/dataquery.cue Co-authored-by: sam boyer <sdboyer@grafana.com> * make gen-cue * Add comments * Make linter happy * Remove editormode override * Update --------- Co-authored-by: sam boyer <sdboyer@grafana.com> Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
parent
473013e3f5
commit
68b588b912
@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
keywords:
|
||||||
|
- grafana
|
||||||
|
- schema
|
||||||
|
title: PrometheusDataQuery kind
|
||||||
|
---
|
||||||
|
> Both documentation generation and kinds schemas are in active development and subject to change without prior notice.
|
||||||
|
|
||||||
|
## PrometheusDataQuery
|
||||||
|
|
||||||
|
#### Maturity: [experimental](../../../maturity/#experimental)
|
||||||
|
#### Version: 0.0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
It extends [DataQuery](#dataquery).
|
||||||
|
|
||||||
|
| Property | Type | Required | Description |
|
||||||
|
|--------------|---------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `expr` | string | **Yes** | The actual expression/query that will be evaluated by Prometheus |
|
||||||
|
| `refId` | string | **Yes** | *(Inherited from [DataQuery](#dataquery))*<br/>A unique identifier for the query within the list of targets.<br/>In server side expressions, the refId is used as a variable name to identify results.<br/>By default, the UI will assign A->Z; however setting meaningful names may be useful. |
|
||||||
|
| `datasource` | | No | *(Inherited from [DataQuery](#dataquery))*<br/>For mixed data sources the selected datasource is on the query level.<br/>For non mixed scenarios this is undefined.<br/>TODO find a better way to do this ^ that's friendly to schema<br/>TODO this shouldn't be unknown but DataSourceRef | null |
|
||||||
|
| `editorMode` | string | No | Possible values are: `code`, `builder`. |
|
||||||
|
| `exemplar` | boolean | No | Execute an additional query to identify interesting raw samples relevant for the given expr |
|
||||||
|
| `format` | string | No | Possible values are: `time_series`, `table`, `heatmap`. |
|
||||||
|
| `hide` | boolean | No | *(Inherited from [DataQuery](#dataquery))*<br/>true if query is disabled (ie should not be returned to the dashboard)<br/>Note this does not always imply that the query should not be executed since<br/>the results from a hidden query may be used as the input to other queries (SSE etc) |
|
||||||
|
| `instant` | boolean | No | Returns only the latest value that Prometheus has scraped for the requested time series |
|
||||||
|
| `queryType` | string | No | *(Inherited from [DataQuery](#dataquery))*<br/>Specify the query flavor<br/>TODO make this required and give it a default |
|
||||||
|
| `range` | boolean | No | Returns a Range vector, comprised of a set of time series containing a range of data points over time for each time series |
|
||||||
|
|
||||||
|
### DataQuery
|
||||||
|
|
||||||
|
These are the common properties available to all queries in all datasources.
|
||||||
|
Specific implementations will *extend* this interface, adding the required
|
||||||
|
properties for the given context.
|
||||||
|
|
||||||
|
| Property | Type | Required | Description |
|
||||||
|
|--------------|---------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `refId` | string | **Yes** | A unique identifier for the query within the list of targets.<br/>In server side expressions, the refId is used as a variable name to identify results.<br/>By default, the UI will assign A->Z; however setting meaningful names may be useful. |
|
||||||
|
| `datasource` | | No | For mixed data sources the selected datasource is on the query level.<br/>For non mixed scenarios this is undefined.<br/>TODO find a better way to do this ^ that's friendly to schema<br/>TODO this shouldn't be unknown but DataSourceRef | null |
|
||||||
|
| `hide` | boolean | No | true if query is disabled (ie should not be returned to the dashboard)<br/>Note this does not always imply that the query should not be executed since<br/>the results from a hidden query may be used as the input to other queries (SSE etc) |
|
||||||
|
| `queryType` | string | No | Specify the query flavor<br/>TODO make this required and give it a default |
|
||||||
|
|
||||||
|
|
@ -1346,7 +1346,9 @@
|
|||||||
},
|
},
|
||||||
"prometheusdataquery": {
|
"prometheusdataquery": {
|
||||||
"category": "composable",
|
"category": "composable",
|
||||||
"codeowners": [],
|
"codeowners": [
|
||||||
|
"grafana/observability-metrics"
|
||||||
|
],
|
||||||
"currentVersion": [
|
"currentVersion": [
|
||||||
0,
|
0,
|
||||||
0
|
0
|
||||||
@ -1354,13 +1356,13 @@
|
|||||||
"grafanaMaturityCount": 0,
|
"grafanaMaturityCount": 0,
|
||||||
"lineageIsGroup": false,
|
"lineageIsGroup": false,
|
||||||
"links": {
|
"links": {
|
||||||
"docs": "n/a",
|
"docs": "https://grafana.com/docs/grafana/next/developers/kinds/composable/prometheusdataquery/schema-reference",
|
||||||
"go": "n/a",
|
"go": "https://github.com/grafana/grafana/tree/main/pkg/tsdb/prometheus/kinds/dataquery/types_dataquery_gen.go",
|
||||||
"schema": "n/a",
|
"schema": "https://github.com/grafana/grafana/tree/main/public/app/plugins/datasource/prometheus/dataquery.cue",
|
||||||
"ts": "n/a"
|
"ts": "https://github.com/grafana/grafana/tree/main/public/app/plugins/datasource/prometheus/dataquery.gen.ts"
|
||||||
},
|
},
|
||||||
"machineName": "prometheusdataquery",
|
"machineName": "prometheusdataquery",
|
||||||
"maturity": "planned",
|
"maturity": "experimental",
|
||||||
"name": "PrometheusDataQuery",
|
"name": "PrometheusDataQuery",
|
||||||
"pluralMachineName": "prometheusdataquerys",
|
"pluralMachineName": "prometheusdataquerys",
|
||||||
"pluralName": "PrometheusDataQuerys",
|
"pluralName": "PrometheusDataQuerys",
|
||||||
@ -2053,6 +2055,7 @@
|
|||||||
"parcadataquery",
|
"parcadataquery",
|
||||||
"phlaredataquery",
|
"phlaredataquery",
|
||||||
"piechartpanelcfg",
|
"piechartpanelcfg",
|
||||||
|
"prometheusdataquery",
|
||||||
"statetimelinepanelcfg",
|
"statetimelinepanelcfg",
|
||||||
"statpanelcfg",
|
"statpanelcfg",
|
||||||
"statushistorypanelcfg",
|
"statushistorypanelcfg",
|
||||||
@ -2062,7 +2065,7 @@
|
|||||||
"textpanelcfg",
|
"textpanelcfg",
|
||||||
"xychartpanelcfg"
|
"xychartpanelcfg"
|
||||||
],
|
],
|
||||||
"count": 26
|
"count": 27
|
||||||
},
|
},
|
||||||
"mature": {
|
"mature": {
|
||||||
"name": "mature",
|
"name": "mature",
|
||||||
@ -2120,7 +2123,6 @@
|
|||||||
"phlaredatasourcecfg",
|
"phlaredatasourcecfg",
|
||||||
"postgresqldataquery",
|
"postgresqldataquery",
|
||||||
"postgresqldatasourcecfg",
|
"postgresqldatasourcecfg",
|
||||||
"prometheusdataquery",
|
|
||||||
"prometheusdatasourcecfg",
|
"prometheusdatasourcecfg",
|
||||||
"query",
|
"query",
|
||||||
"queryhistory",
|
"queryhistory",
|
||||||
@ -2134,7 +2136,7 @@
|
|||||||
"zipkindataquery",
|
"zipkindataquery",
|
||||||
"zipkindatasourcecfg"
|
"zipkindatasourcecfg"
|
||||||
],
|
],
|
||||||
"count": 46
|
"count": 45
|
||||||
},
|
},
|
||||||
"stable": {
|
"stable": {
|
||||||
"name": "stable",
|
"name": "stable",
|
||||||
|
89
pkg/tsdb/prometheus/kinds/dataquery/types_dataquery_gen.go
Normal file
89
pkg/tsdb/prometheus/kinds/dataquery/types_dataquery_gen.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||||
|
//
|
||||||
|
// Generated by:
|
||||||
|
// public/app/plugins/gen.go
|
||||||
|
// Using jennies:
|
||||||
|
// PluginGoTypesJenny
|
||||||
|
//
|
||||||
|
// Run 'make gen-cue' from repository root to regenerate.
|
||||||
|
|
||||||
|
package dataquery
|
||||||
|
|
||||||
|
// Defines values for PromQueryFormat.
|
||||||
|
const (
|
||||||
|
PromQueryFormatHeatmap PromQueryFormat = "heatmap"
|
||||||
|
PromQueryFormatTable PromQueryFormat = "table"
|
||||||
|
PromQueryFormatTimeSeries PromQueryFormat = "time_series"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Defines values for EditorMode.
|
||||||
|
const (
|
||||||
|
EditorModeBuilder EditorMode = "builder"
|
||||||
|
EditorModeCode EditorMode = "code"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Defines values for Format.
|
||||||
|
const (
|
||||||
|
FormatHeatmap Format = "heatmap"
|
||||||
|
FormatTable Format = "table"
|
||||||
|
FormatTimeSeries Format = "time_series"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Defines values for QueryEditorMode.
|
||||||
|
const (
|
||||||
|
QueryEditorModeBuilder QueryEditorMode = "builder"
|
||||||
|
QueryEditorModeCode QueryEditorMode = "code"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PromQueryFormat defines model for PromQueryFormat.
|
||||||
|
type PromQueryFormat string
|
||||||
|
|
||||||
|
// PrometheusDataQuery defines model for PrometheusDataQuery.
|
||||||
|
type PrometheusDataQuery struct {
|
||||||
|
// For mixed data sources the selected datasource is on the query level.
|
||||||
|
// For non mixed scenarios this is undefined.
|
||||||
|
// TODO find a better way to do this ^ that's friendly to schema
|
||||||
|
// TODO this shouldn't be unknown but DataSourceRef | null
|
||||||
|
Datasource *interface{} `json:"datasource,omitempty"`
|
||||||
|
|
||||||
|
// Specifies which editor is being used to prepare the query. It can be "code" or "builder"
|
||||||
|
EditorMode *EditorMode `json:"editorMode,omitempty"`
|
||||||
|
|
||||||
|
// Execute an additional query to identify interesting raw samples relevant for the given expr
|
||||||
|
Exemplar *bool `json:"exemplar,omitempty"`
|
||||||
|
|
||||||
|
// The actual expression/query that will be evaluated by Prometheus
|
||||||
|
Expr string `json:"expr"`
|
||||||
|
|
||||||
|
// Query format to determine how to display data points in panel. It can be "time_series", "table", "heatmap"
|
||||||
|
Format *Format `json:"format,omitempty"`
|
||||||
|
|
||||||
|
// Hide true if query is disabled (ie should not be returned to the dashboard)
|
||||||
|
// Note this does not always imply that the query should not be executed since
|
||||||
|
// the results from a hidden query may be used as the input to other queries (SSE etc)
|
||||||
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
|
// Returns only the latest value that Prometheus has scraped for the requested time series
|
||||||
|
Instant *bool `json:"instant,omitempty"`
|
||||||
|
|
||||||
|
// Specify the query flavor
|
||||||
|
// TODO make this required and give it a default
|
||||||
|
QueryType *string `json:"queryType,omitempty"`
|
||||||
|
|
||||||
|
// Returns a Range vector, comprised of a set of time series containing a range of data points over time for each time series
|
||||||
|
Range *bool `json:"range,omitempty"`
|
||||||
|
|
||||||
|
// A unique identifier for the query within the list of targets.
|
||||||
|
// In server side expressions, the refId is used as a variable name to identify results.
|
||||||
|
// By default, the UI will assign A->Z; however setting meaningful names may be useful.
|
||||||
|
RefId string `json:"refId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specifies which editor is being used to prepare the query. It can be "code" or "builder"
|
||||||
|
type EditorMode string
|
||||||
|
|
||||||
|
// Query format to determine how to display data points in panel. It can be "time_series", "table", "heatmap"
|
||||||
|
type Format string
|
||||||
|
|
||||||
|
// QueryEditorMode defines model for QueryEditorMode.
|
||||||
|
type QueryEditorMode string
|
@ -8,8 +8,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/prometheus/kinds/dataquery"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Internal interval and range variables
|
// Internal interval and range variables
|
||||||
@ -45,16 +45,14 @@ const (
|
|||||||
var safeResolution = 11000
|
var safeResolution = 11000
|
||||||
|
|
||||||
type QueryModel struct {
|
type QueryModel struct {
|
||||||
Expr string `json:"expr"`
|
dataquery.PrometheusDataQuery
|
||||||
LegendFormat string `json:"legendFormat"`
|
// The following properties may be part of the request payload, however they are not saved in panel JSON
|
||||||
Interval string `json:"interval"`
|
// Timezone offset to align start & end time on backend
|
||||||
IntervalMS int64 `json:"intervalMS"`
|
UtcOffsetSec int64 `json:"utcOffsetSec,omitempty"`
|
||||||
StepMode string `json:"stepMode"`
|
LegendFormat string `json:"legendFormat,omitempty"`
|
||||||
RangeQuery bool `json:"range"`
|
Interval string `json:"interval,omitempty"`
|
||||||
InstantQuery bool `json:"instant"`
|
IntervalMs int64 `json:"intervalMs,omitempty"`
|
||||||
ExemplarQuery bool `json:"exemplar"`
|
IntervalFactor int64 `json:"intervalFactor,omitempty"`
|
||||||
IntervalFactor int64 `json:"intervalFactor"`
|
|
||||||
UtcOffsetSec int64 `json:"utcOffsetSec"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TimeRange struct {
|
type TimeRange struct {
|
||||||
@ -82,23 +80,36 @@ func Parse(query backend.DataQuery, timeInterval string, intervalCalculator inte
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
//Final interval value
|
// Final interval value
|
||||||
interval, err := calculatePrometheusInterval(model, timeInterval, query, intervalCalculator)
|
interval, err := calculatePrometheusInterval(model.Interval, timeInterval, model.IntervalMs, model.IntervalFactor, query, intervalCalculator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interpolate variables in expr
|
// Interpolate variables in expr
|
||||||
timeRange := query.TimeRange.To.Sub(query.TimeRange.From)
|
timeRange := query.TimeRange.To.Sub(query.TimeRange.From)
|
||||||
expr := interpolateVariables(model, interval, timeRange, intervalCalculator, timeInterval)
|
expr := interpolateVariables(model.Expr, model.Interval, interval, timeRange, intervalCalculator, timeInterval)
|
||||||
rangeQuery := model.RangeQuery
|
var rangeQuery, instantQuery bool
|
||||||
if !model.InstantQuery && !model.RangeQuery {
|
if model.Instant == nil {
|
||||||
|
instantQuery = false
|
||||||
|
} else {
|
||||||
|
instantQuery = *model.Instant
|
||||||
|
}
|
||||||
|
if model.Range == nil {
|
||||||
|
rangeQuery = false
|
||||||
|
} else {
|
||||||
|
rangeQuery = *model.Range
|
||||||
|
}
|
||||||
|
if !instantQuery && !rangeQuery {
|
||||||
// In older dashboards, we were not setting range query param and !range && !instant was run as range query
|
// In older dashboards, we were not setting range query param and !range && !instant was run as range query
|
||||||
rangeQuery = true
|
rangeQuery = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// We never want to run exemplar query for alerting
|
// We never want to run exemplar query for alerting
|
||||||
exemplarQuery := model.ExemplarQuery
|
exemplarQuery := false
|
||||||
|
if model.Exemplar != nil {
|
||||||
|
exemplarQuery = *model.Exemplar
|
||||||
|
}
|
||||||
if fromAlert {
|
if fromAlert {
|
||||||
exemplarQuery = false
|
exemplarQuery = false
|
||||||
}
|
}
|
||||||
@ -110,7 +121,7 @@ func Parse(query backend.DataQuery, timeInterval string, intervalCalculator inte
|
|||||||
Start: query.TimeRange.From,
|
Start: query.TimeRange.From,
|
||||||
End: query.TimeRange.To,
|
End: query.TimeRange.To,
|
||||||
RefId: query.RefID,
|
RefId: query.RefID,
|
||||||
InstantQuery: model.InstantQuery,
|
InstantQuery: instantQuery,
|
||||||
RangeQuery: rangeQuery,
|
RangeQuery: rangeQuery,
|
||||||
ExemplarQuery: exemplarQuery,
|
ExemplarQuery: exemplarQuery,
|
||||||
UtcOffsetSec: model.UtcOffsetSec,
|
UtcOffsetSec: model.UtcOffsetSec,
|
||||||
@ -139,15 +150,18 @@ func (query *Query) TimeRange() TimeRange {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculatePrometheusInterval(model *QueryModel, timeInterval string, query backend.DataQuery, intervalCalculator intervalv2.Calculator) (time.Duration, error) {
|
func calculatePrometheusInterval(
|
||||||
queryInterval := model.Interval
|
queryInterval, timeInterval string,
|
||||||
|
intervalMs, intervalFactor int64,
|
||||||
//If we are using variable for interval/step, we will replace it with calculated interval
|
query backend.DataQuery,
|
||||||
|
intervalCalculator intervalv2.Calculator,
|
||||||
|
) (time.Duration, error) {
|
||||||
|
// If we are using variable for interval/step, we will replace it with calculated interval
|
||||||
if isVariableInterval(queryInterval) {
|
if isVariableInterval(queryInterval) {
|
||||||
queryInterval = ""
|
queryInterval = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
minInterval, err := intervalv2.GetIntervalFrom(timeInterval, queryInterval, model.IntervalMS, 15*time.Second)
|
minInterval, err := intervalv2.GetIntervalFrom(timeInterval, queryInterval, intervalMs, 15*time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return time.Duration(0), err
|
return time.Duration(0), err
|
||||||
}
|
}
|
||||||
@ -159,19 +173,23 @@ func calculatePrometheusInterval(model *QueryModel, timeInterval string, query b
|
|||||||
adjustedInterval = calculatedInterval.Value
|
adjustedInterval = calculatedInterval.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
if model.Interval == varRateInterval || model.Interval == varRateIntervalAlt {
|
if queryInterval == varRateInterval || queryInterval == varRateIntervalAlt {
|
||||||
// Rate interval is final and is not affected by resolution
|
// Rate interval is final and is not affected by resolution
|
||||||
return calculateRateInterval(adjustedInterval, timeInterval, intervalCalculator), nil
|
return calculateRateInterval(adjustedInterval, timeInterval, intervalCalculator), nil
|
||||||
} else {
|
} else {
|
||||||
intervalFactor := model.IntervalFactor
|
queryIntervalFactor := intervalFactor
|
||||||
if intervalFactor == 0 {
|
if queryIntervalFactor == 0 {
|
||||||
intervalFactor = 1
|
queryIntervalFactor = 1
|
||||||
}
|
}
|
||||||
return time.Duration(int64(adjustedInterval) * intervalFactor), nil
|
return time.Duration(int64(adjustedInterval) * queryIntervalFactor), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateRateInterval(interval time.Duration, scrapeInterval string, intervalCalculator intervalv2.Calculator) time.Duration {
|
func calculateRateInterval(
|
||||||
|
interval time.Duration,
|
||||||
|
scrapeInterval string,
|
||||||
|
intervalCalculator intervalv2.Calculator,
|
||||||
|
) time.Duration {
|
||||||
scrape := scrapeInterval
|
scrape := scrapeInterval
|
||||||
if scrape == "" {
|
if scrape == "" {
|
||||||
scrape = "15s"
|
scrape = "15s"
|
||||||
@ -186,13 +204,14 @@ func calculateRateInterval(interval time.Duration, scrapeInterval string, interv
|
|||||||
return rateInterval
|
return rateInterval
|
||||||
}
|
}
|
||||||
|
|
||||||
func interpolateVariables(model *QueryModel, interval time.Duration, timeRange time.Duration, intervalCalculator intervalv2.Calculator, timeInterval string) string {
|
func interpolateVariables(expr, queryInterval string, interval time.Duration,
|
||||||
expr := model.Expr
|
timeRange time.Duration,
|
||||||
|
intervalCalculator intervalv2.Calculator, timeInterval string) string {
|
||||||
rangeMs := timeRange.Milliseconds()
|
rangeMs := timeRange.Milliseconds()
|
||||||
rangeSRounded := int64(math.Round(float64(rangeMs) / 1000.0))
|
rangeSRounded := int64(math.Round(float64(rangeMs) / 1000.0))
|
||||||
|
|
||||||
var rateInterval time.Duration
|
var rateInterval time.Duration
|
||||||
if model.Interval == varRateInterval || model.Interval == varRateIntervalAlt {
|
if queryInterval == varRateInterval || queryInterval == varRateIntervalAlt {
|
||||||
rateInterval = interval
|
rateInterval = interval
|
||||||
} else {
|
} else {
|
||||||
rateInterval = calculateRateInterval(interval, timeInterval, intervalCalculator)
|
rateInterval = calculateRateInterval(interval, timeInterval, intervalCalculator)
|
||||||
@ -219,7 +238,7 @@ func isVariableInterval(interval string) bool {
|
|||||||
if interval == varInterval || interval == varIntervalMs || interval == varRateInterval {
|
if interval == varInterval || interval == varIntervalMs || interval == varRateInterval {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
//Repetitive code, we should have functionality to unify these
|
// Repetitive code, we should have functionality to unify these
|
||||||
if interval == varIntervalAlt || interval == varIntervalMsAlt || interval == varRateIntervalAlt {
|
if interval == varIntervalAlt || interval == varIntervalMsAlt || interval == varRateIntervalAlt {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -376,6 +376,7 @@ func TestParse(t *testing.T) {
|
|||||||
"format": "time_series",
|
"format": "time_series",
|
||||||
"intervalFactor": 1,
|
"intervalFactor": 1,
|
||||||
"interval": "$__rate_interval",
|
"interval": "$__rate_interval",
|
||||||
|
"intervalMs": 60000,
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}`, timeRange)
|
}`, timeRange)
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
|
"github.com/grafana/grafana/pkg/kindsys"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/prometheus/kinds/dataquery"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/prometheus/models"
|
"github.com/grafana/grafana/pkg/tsdb/prometheus/models"
|
||||||
@ -121,8 +123,10 @@ func createJsonTestData(start int64, step int64, timestampCount int, seriesCount
|
|||||||
bytes := []byte(fmt.Sprintf(`{"status":"success","data":{"resultType":"matrix","result":[%v]}}`, strings.Join(allSeries, ",")))
|
bytes := []byte(fmt.Sprintf(`{"status":"success","data":{"resultType":"matrix","result":[%v]}}`, strings.Join(allSeries, ",")))
|
||||||
|
|
||||||
qm := models.QueryModel{
|
qm := models.QueryModel{
|
||||||
RangeQuery: true,
|
PrometheusDataQuery: dataquery.PrometheusDataQuery{
|
||||||
Expr: "test",
|
Range: kindsys.Ptr(true),
|
||||||
|
Expr: "test",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := json.Marshal(&qm)
|
data, err := json.Marshal(&qm)
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/experimental"
|
"github.com/grafana/grafana-plugin-sdk-go/experimental"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/prometheus/kinds/dataquery"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/prometheus/models"
|
"github.com/grafana/grafana/pkg/tsdb/prometheus/models"
|
||||||
@ -115,12 +116,14 @@ func loadStoredQuery(fileName string) (*backend.QueryDataRequest, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
qm := models.QueryModel{
|
qm := models.QueryModel{
|
||||||
RangeQuery: sq.RangeQuery,
|
PrometheusDataQuery: dataquery.PrometheusDataQuery{
|
||||||
ExemplarQuery: sq.ExemplarQuery,
|
Range: &sq.RangeQuery,
|
||||||
Expr: sq.Expr,
|
Exemplar: &sq.ExemplarQuery,
|
||||||
Interval: fmt.Sprintf("%ds", sq.Step),
|
Expr: sq.Expr,
|
||||||
IntervalMS: sq.Step * 1000,
|
},
|
||||||
LegendFormat: sq.LegendFormat,
|
Interval: fmt.Sprintf("%ds", sq.Step),
|
||||||
|
IntervalMs: sq.Step * 1000,
|
||||||
|
LegendFormat: sq.LegendFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := json.Marshal(&qm)
|
data, err := json.Marshal(&qm)
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -13,6 +14,7 @@ import (
|
|||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/prometheus/kinds/dataquery"
|
||||||
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
|
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
|
||||||
p "github.com/prometheus/common/model"
|
p "github.com/prometheus/common/model"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -20,6 +22,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||||
"github.com/grafana/grafana/pkg/infra/log/logtest"
|
"github.com/grafana/grafana/pkg/infra/log/logtest"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
|
"github.com/grafana/grafana/pkg/kindsys"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/prometheus/client"
|
"github.com/grafana/grafana/pkg/tsdb/prometheus/client"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/prometheus/models"
|
"github.com/grafana/grafana/pkg/tsdb/prometheus/models"
|
||||||
@ -65,9 +68,11 @@ func TestPrometheus_parseTimeSeriesResponse(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
qm := models.QueryModel{
|
qm := models.QueryModel{
|
||||||
LegendFormat: "legend {{app}}",
|
LegendFormat: "legend {{app}}",
|
||||||
UtcOffsetSec: 0,
|
UtcOffsetSec: 0,
|
||||||
ExemplarQuery: true,
|
PrometheusDataQuery: dataquery.PrometheusDataQuery{
|
||||||
|
Exemplar: kindsys.Ptr(true),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
b, err := json.Marshal(&qm)
|
b, err := json.Marshal(&qm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -112,7 +117,9 @@ func TestPrometheus_parseTimeSeriesResponse(t *testing.T) {
|
|||||||
qm := models.QueryModel{
|
qm := models.QueryModel{
|
||||||
LegendFormat: "legend {{app}}",
|
LegendFormat: "legend {{app}}",
|
||||||
UtcOffsetSec: 0,
|
UtcOffsetSec: 0,
|
||||||
RangeQuery: true,
|
PrometheusDataQuery: dataquery.PrometheusDataQuery{
|
||||||
|
Range: kindsys.Ptr(true),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
b, err := json.Marshal(&qm)
|
b, err := json.Marshal(&qm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -159,7 +166,9 @@ func TestPrometheus_parseTimeSeriesResponse(t *testing.T) {
|
|||||||
qm := models.QueryModel{
|
qm := models.QueryModel{
|
||||||
LegendFormat: "",
|
LegendFormat: "",
|
||||||
UtcOffsetSec: 0,
|
UtcOffsetSec: 0,
|
||||||
RangeQuery: true,
|
PrometheusDataQuery: dataquery.PrometheusDataQuery{
|
||||||
|
Range: kindsys.Ptr(true),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
b, err := json.Marshal(&qm)
|
b, err := json.Marshal(&qm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -202,7 +211,9 @@ func TestPrometheus_parseTimeSeriesResponse(t *testing.T) {
|
|||||||
qm := models.QueryModel{
|
qm := models.QueryModel{
|
||||||
LegendFormat: "",
|
LegendFormat: "",
|
||||||
UtcOffsetSec: 0,
|
UtcOffsetSec: 0,
|
||||||
RangeQuery: true,
|
PrometheusDataQuery: dataquery.PrometheusDataQuery{
|
||||||
|
Range: kindsys.Ptr(true),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
b, err := json.Marshal(&qm)
|
b, err := json.Marshal(&qm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -243,7 +254,9 @@ func TestPrometheus_parseTimeSeriesResponse(t *testing.T) {
|
|||||||
qm := models.QueryModel{
|
qm := models.QueryModel{
|
||||||
LegendFormat: "",
|
LegendFormat: "",
|
||||||
UtcOffsetSec: 0,
|
UtcOffsetSec: 0,
|
||||||
RangeQuery: true,
|
PrometheusDataQuery: dataquery.PrometheusDataQuery{
|
||||||
|
Range: kindsys.Ptr(true),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
b, err := json.Marshal(&qm)
|
b, err := json.Marshal(&qm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -278,7 +291,9 @@ func TestPrometheus_parseTimeSeriesResponse(t *testing.T) {
|
|||||||
qm := models.QueryModel{
|
qm := models.QueryModel{
|
||||||
LegendFormat: "legend {{app}}",
|
LegendFormat: "legend {{app}}",
|
||||||
UtcOffsetSec: 0,
|
UtcOffsetSec: 0,
|
||||||
InstantQuery: true,
|
PrometheusDataQuery: dataquery.PrometheusDataQuery{
|
||||||
|
Instant: kindsys.Ptr(true),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
b, err := json.Marshal(&qm)
|
b, err := json.Marshal(&qm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -317,7 +332,9 @@ func TestPrometheus_parseTimeSeriesResponse(t *testing.T) {
|
|||||||
qm := models.QueryModel{
|
qm := models.QueryModel{
|
||||||
LegendFormat: "",
|
LegendFormat: "",
|
||||||
UtcOffsetSec: 0,
|
UtcOffsetSec: 0,
|
||||||
InstantQuery: true,
|
PrometheusDataQuery: dataquery.PrometheusDataQuery{
|
||||||
|
Instant: kindsys.Ptr(true),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
b, err := json.Marshal(&qm)
|
b, err := json.Marshal(&qm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -354,20 +371,20 @@ func executeWithHeaders(tctx *testContext, query backend.DataQuery, qr interface
|
|||||||
}
|
}
|
||||||
|
|
||||||
promRes, err := toAPIResponse(qr)
|
promRes, err := toAPIResponse(qr)
|
||||||
|
defer func() {
|
||||||
|
if err := promRes.Body.Close(); err != nil {
|
||||||
|
fmt.Println(fmt.Errorf("response body close error: %v", err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tctx.httpProvider.setResponse(promRes)
|
tctx.httpProvider.setResponse(promRes)
|
||||||
|
|
||||||
res, err := tctx.queryData.Execute(context.Background(), &req)
|
res, err := tctx.queryData.Execute(context.Background(), &req)
|
||||||
errClose := promRes.Body.Close()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if errClose != nil {
|
|
||||||
return nil, errClose
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.Responses[req.Queries[0].RefID].Frames, nil
|
return res.Responses[req.Queries[0].RefID].Frames, nil
|
||||||
}
|
}
|
||||||
|
55
public/app/plugins/datasource/prometheus/dataquery.cue
Normal file
55
public/app/plugins/datasource/prometheus/dataquery.cue
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2023 Grafana Labs
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package grafanaplugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
common "github.com/grafana/grafana/packages/grafana-schema/src/common"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins/pfs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This file (with its sibling .cue files) implements pfs.GrafanaPlugin
|
||||||
|
pfs.GrafanaPlugin
|
||||||
|
|
||||||
|
composableKinds: DataQuery: {
|
||||||
|
maturity: "experimental"
|
||||||
|
|
||||||
|
lineage: {
|
||||||
|
seqs: [
|
||||||
|
{
|
||||||
|
schemas: [
|
||||||
|
{
|
||||||
|
common.DataQuery
|
||||||
|
|
||||||
|
// The actual expression/query that will be evaluated by Prometheus
|
||||||
|
expr: string
|
||||||
|
// Returns only the latest value that Prometheus has scraped for the requested time series
|
||||||
|
instant?: bool
|
||||||
|
// Returns a Range vector, comprised of a set of time series containing a range of data points over time for each time series
|
||||||
|
range?: bool
|
||||||
|
// Execute an additional query to identify interesting raw samples relevant for the given expr
|
||||||
|
exemplar?: bool
|
||||||
|
// Specifies which editor is being used to prepare the query. It can be "code" or "builder"
|
||||||
|
editorMode?: #QueryEditorMode
|
||||||
|
// Query format to determine how to display data points in panel. It can be "time_series", "table", "heatmap"
|
||||||
|
format?: #PromQueryFormat
|
||||||
|
|
||||||
|
#QueryEditorMode: "code" | "builder" @cuetsy(kind="enum")
|
||||||
|
#PromQueryFormat: "time_series" | "table" | "heatmap" @cuetsy(kind="type")
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
47
public/app/plugins/datasource/prometheus/dataquery.gen.ts
Normal file
47
public/app/plugins/datasource/prometheus/dataquery.gen.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||||
|
//
|
||||||
|
// Generated by:
|
||||||
|
// public/app/plugins/gen.go
|
||||||
|
// Using jennies:
|
||||||
|
// TSTypesJenny
|
||||||
|
// PluginTSTypesJenny
|
||||||
|
//
|
||||||
|
// Run 'make gen-cue' from repository root to regenerate.
|
||||||
|
|
||||||
|
import * as common from '@grafana/schema';
|
||||||
|
|
||||||
|
export const DataQueryModelVersion = Object.freeze([0, 0]);
|
||||||
|
|
||||||
|
export enum QueryEditorMode {
|
||||||
|
Builder = 'builder',
|
||||||
|
Code = 'code',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PromQueryFormat = ('time_series' | 'table' | 'heatmap');
|
||||||
|
|
||||||
|
export interface Prometheus extends common.DataQuery {
|
||||||
|
/**
|
||||||
|
* Specifies which editor is being used to prepare the query. It can be "code" or "builder"
|
||||||
|
*/
|
||||||
|
editorMode?: QueryEditorMode;
|
||||||
|
/**
|
||||||
|
* Execute an additional query to identify interesting raw samples relevant for the given expr
|
||||||
|
*/
|
||||||
|
exemplar?: boolean;
|
||||||
|
/**
|
||||||
|
* The actual expression/query that will be evaluated by Prometheus
|
||||||
|
*/
|
||||||
|
expr: string;
|
||||||
|
/**
|
||||||
|
* Query format to determine how to display data points in panel. It can be "time_series", "table", "heatmap"
|
||||||
|
*/
|
||||||
|
format?: PromQueryFormat;
|
||||||
|
/**
|
||||||
|
* Returns only the latest value that Prometheus has scraped for the requested time series
|
||||||
|
*/
|
||||||
|
instant?: boolean;
|
||||||
|
/**
|
||||||
|
* Returns a Range vector, comprised of a set of time series containing a range of data points over time for each time series
|
||||||
|
*/
|
||||||
|
range?: boolean;
|
||||||
|
}
|
@ -190,7 +190,7 @@ describe('PrometheusDatasource', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('customQueryParams', () => {
|
describe('customQueryParams', () => {
|
||||||
const target = { expr: 'test{job="testjob"}', format: 'time_series', refId: '' };
|
const target: PromQuery = { expr: 'test{job="testjob"}', format: 'time_series', refId: '' };
|
||||||
|
|
||||||
function makeQuery(target: PromQuery) {
|
function makeQuery(target: PromQuery) {
|
||||||
return {
|
return {
|
||||||
@ -618,7 +618,7 @@ describe('PrometheusDatasource', () => {
|
|||||||
|
|
||||||
describe('interpolateVariablesInQueries', () => {
|
describe('interpolateVariablesInQueries', () => {
|
||||||
it('should call replace function 2 times', () => {
|
it('should call replace function 2 times', () => {
|
||||||
const query = {
|
const query: PromQuery = {
|
||||||
expr: 'test{job="testjob"}',
|
expr: 'test{job="testjob"}',
|
||||||
format: 'time_series',
|
format: 'time_series',
|
||||||
interval: '$Interval',
|
interval: '$Interval',
|
||||||
|
@ -5,6 +5,7 @@ import { EditorField, EditorRow, EditorSwitch } from '@grafana/experimental';
|
|||||||
import { AutoSizeInput, RadioButtonGroup, Select } from '@grafana/ui';
|
import { AutoSizeInput, RadioButtonGroup, Select } from '@grafana/ui';
|
||||||
|
|
||||||
import { getQueryTypeChangeHandler, getQueryTypeOptions } from '../../components/PromExploreExtraField';
|
import { getQueryTypeChangeHandler, getQueryTypeOptions } from '../../components/PromExploreExtraField';
|
||||||
|
import { PromQueryFormat } from '../../dataquery.gen';
|
||||||
import { PromQuery } from '../../types';
|
import { PromQuery } from '../../types';
|
||||||
import { QueryOptionGroup } from '../shared/QueryOptionGroup';
|
import { QueryOptionGroup } from '../shared/QueryOptionGroup';
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ export interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const PromQueryBuilderOptions = React.memo<Props>(({ query, app, onChange, onRunQuery }) => {
|
export const PromQueryBuilderOptions = React.memo<Props>(({ query, app, onChange, onRunQuery }) => {
|
||||||
const onChangeFormat = (value: SelectableValue<string>) => {
|
const onChangeFormat = (value: SelectableValue<PromQueryFormat>) => {
|
||||||
onChange({ ...query, format: value.value });
|
onChange({ ...query, format: value.value });
|
||||||
onRunQuery();
|
onRunQuery();
|
||||||
};
|
};
|
||||||
|
@ -8,6 +8,7 @@ import { reportInteraction } from '@grafana/runtime';
|
|||||||
import { Button, ConfirmModal } from '@grafana/ui';
|
import { Button, ConfirmModal } from '@grafana/ui';
|
||||||
|
|
||||||
import { PromQueryEditorProps } from '../../components/types';
|
import { PromQueryEditorProps } from '../../components/types';
|
||||||
|
import { PromQueryFormat } from '../../dataquery.gen';
|
||||||
import { PromQuery } from '../../types';
|
import { PromQuery } from '../../types';
|
||||||
import { QueryPatternsModal } from '../QueryPatternsModal';
|
import { QueryPatternsModal } from '../QueryPatternsModal';
|
||||||
import { buildVisualQueryFromString } from '../parsing';
|
import { buildVisualQueryFromString } from '../parsing';
|
||||||
@ -21,7 +22,7 @@ import { PromQueryBuilderContainer } from './PromQueryBuilderContainer';
|
|||||||
import { PromQueryBuilderOptions } from './PromQueryBuilderOptions';
|
import { PromQueryBuilderOptions } from './PromQueryBuilderOptions';
|
||||||
import { PromQueryCodeEditor } from './PromQueryCodeEditor';
|
import { PromQueryCodeEditor } from './PromQueryCodeEditor';
|
||||||
|
|
||||||
export const FORMAT_OPTIONS: Array<SelectableValue<string>> = [
|
export const FORMAT_OPTIONS: Array<SelectableValue<PromQueryFormat>> = [
|
||||||
{ label: 'Time series', value: 'time_series' },
|
{ label: 'Time series', value: 'time_series' },
|
||||||
{ label: 'Table', value: 'table' },
|
{ label: 'Table', value: 'table' },
|
||||||
{ label: 'Heatmap', value: 'heatmap' },
|
{ label: 'Heatmap', value: 'heatmap' },
|
||||||
|
@ -1,27 +1,24 @@
|
|||||||
import { DataQuery, DataSourceJsonData, QueryResultMeta, ScopedVars } from '@grafana/data';
|
import { DataSourceJsonData, QueryResultMeta, ScopedVars } from '@grafana/data';
|
||||||
|
import { DataQuery } from '@grafana/schema';
|
||||||
|
|
||||||
import { PromApplication } from '../../../types/unified-alerting-dto';
|
import { PromApplication } from '../../../types/unified-alerting-dto';
|
||||||
|
|
||||||
|
import { Prometheus as GenPromQuery } from './dataquery.gen';
|
||||||
import { QueryEditorMode } from './querybuilder/shared/types';
|
import { QueryEditorMode } from './querybuilder/shared/types';
|
||||||
|
|
||||||
export interface PromQuery extends DataQuery {
|
export interface PromQuery extends GenPromQuery, DataQuery {
|
||||||
expr: string;
|
/**
|
||||||
format?: string;
|
* Timezone offset to align start & end time on backend
|
||||||
instant?: boolean;
|
*/
|
||||||
range?: boolean;
|
|
||||||
exemplar?: boolean;
|
|
||||||
hinting?: boolean;
|
|
||||||
interval?: string;
|
|
||||||
intervalFactor?: number;
|
|
||||||
// Timezone offset to align start & end time on backend
|
|
||||||
utcOffsetSec?: number;
|
utcOffsetSec?: number;
|
||||||
legendFormat?: string;
|
legendFormat?: string;
|
||||||
valueWithRefId?: boolean;
|
valueWithRefId?: boolean;
|
||||||
requestId?: string;
|
requestId?: string;
|
||||||
showingGraph?: boolean;
|
showingGraph?: boolean;
|
||||||
showingTable?: boolean;
|
showingTable?: boolean;
|
||||||
/** Code, Builder or Explain */
|
hinting?: boolean;
|
||||||
editorMode?: QueryEditorMode;
|
interval?: string;
|
||||||
|
intervalFactor?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PromOptions extends DataSourceJsonData {
|
export interface PromOptions extends DataSourceJsonData {
|
||||||
|
Loading…
Reference in New Issue
Block a user