mirror of
https://github.com/grafana/grafana.git
synced 2025-01-27 00:37:04 -06:00
CloudWatch: Use generated type from schema in backend (#66420)
* CloudWatch: Use generated type from schema in backend * address comments * don't explicity set default metric query type
This commit is contained in:
parent
255d8f3326
commit
a21fdd9c81
@ -28,15 +28,20 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(pluginCtx backend.PluginCont
|
||||
}
|
||||
|
||||
var period int64
|
||||
if model.Period != "" {
|
||||
p, err := strconv.ParseInt(model.Period, 10, 64)
|
||||
|
||||
if model.Period != nil && *model.Period != "" {
|
||||
p, err := strconv.ParseInt(*model.Period, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
period = p
|
||||
}
|
||||
|
||||
if period == 0 && !model.PrefixMatching {
|
||||
prefixMatching := false
|
||||
if model.PrefixMatching != nil {
|
||||
prefixMatching = *model.PrefixMatching
|
||||
}
|
||||
if period == 0 && !prefixMatching {
|
||||
period = 300
|
||||
}
|
||||
|
||||
@ -49,19 +54,23 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(pluginCtx backend.PluginCont
|
||||
}
|
||||
|
||||
var alarmNames []*string
|
||||
if model.PrefixMatching {
|
||||
metricName := ""
|
||||
if model.MetricName != nil {
|
||||
metricName = *model.MetricName
|
||||
}
|
||||
if prefixMatching {
|
||||
params := &cloudwatch.DescribeAlarmsInput{
|
||||
MaxRecords: aws.Int64(100),
|
||||
ActionPrefix: aws.String(actionPrefix),
|
||||
AlarmNamePrefix: aws.String(alarmNamePrefix),
|
||||
ActionPrefix: actionPrefix,
|
||||
AlarmNamePrefix: alarmNamePrefix,
|
||||
}
|
||||
resp, err := cli.DescribeAlarms(params)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%v: %w", "failed to call cloudwatch:DescribeAlarms", err)
|
||||
}
|
||||
alarmNames = filterAlarms(resp, model.Namespace, model.MetricName, model.Dimensions, statistic, period)
|
||||
alarmNames = filterAlarms(resp, model.Namespace, metricName, model.Dimensions, statistic, period)
|
||||
} else {
|
||||
if model.Region == "" || model.Namespace == "" || model.MetricName == "" || statistic == "" {
|
||||
if model.Region == "" || model.Namespace == "" || metricName == "" || statistic == "" {
|
||||
return result, errors.New("invalid annotations query")
|
||||
}
|
||||
|
||||
@ -80,7 +89,7 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(pluginCtx backend.PluginCont
|
||||
}
|
||||
params := &cloudwatch.DescribeAlarmsForMetricInput{
|
||||
Namespace: aws.String(model.Namespace),
|
||||
MetricName: aws.String(model.MetricName),
|
||||
MetricName: aws.String(metricName),
|
||||
Dimensions: qd,
|
||||
Statistic: aws.String(statistic),
|
||||
Period: aws.Int64(period),
|
||||
|
@ -27,21 +27,13 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/query"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/clients"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
|
||||
)
|
||||
|
||||
type DataQueryJson struct {
|
||||
QueryType string `json:"type,omitempty"`
|
||||
QueryMode string
|
||||
PrefixMatching bool
|
||||
Region string
|
||||
Namespace string
|
||||
MetricName string
|
||||
Dimensions map[string]interface{}
|
||||
Statistic *string
|
||||
Period string
|
||||
ActionPrefix string
|
||||
AlarmNamePrefix string
|
||||
dataquery.CloudWatchAnnotationQuery
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
type DataSource struct {
|
||||
@ -179,7 +171,7 @@ func (e *cloudWatchExecutor) QueryData(ctx context.Context, req *backend.QueryDa
|
||||
}
|
||||
|
||||
var result *backend.QueryDataResponse
|
||||
switch model.QueryType {
|
||||
switch model.Type {
|
||||
case annotationQuery:
|
||||
result, err = e.executeAnnotationQuery(req.PluginContext, model, q)
|
||||
case logAction:
|
||||
|
@ -112,7 +112,7 @@ func (e *cloudWatchExecutor) executeLogAction(ctx context.Context, logger log.Lo
|
||||
}
|
||||
|
||||
var data *data.Frame = nil
|
||||
switch logsQuery.SubType {
|
||||
switch logsQuery.Subtype {
|
||||
case "StartQuery":
|
||||
data, err = e.handleStartQuery(ctx, logger, logsClient, logsQuery, query.TimeRange, query.RefID)
|
||||
case "StopQuery":
|
||||
@ -123,7 +123,7 @@ func (e *cloudWatchExecutor) executeLogAction(ctx context.Context, logger log.Lo
|
||||
data, err = e.handleGetLogEvents(ctx, logsClient, logsQuery)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute log action with subtype: %s: %w", logsQuery.SubType, err)
|
||||
return nil, fmt.Errorf("failed to execute log action with subtype: %s: %w", logsQuery.Subtype, err)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
@ -214,7 +214,7 @@ func (e *cloudWatchExecutor) executeStartQuery(ctx context.Context, logsClient c
|
||||
if logsQuery.LogGroups != nil && len(logsQuery.LogGroups) > 0 {
|
||||
var logGroupIdentifiers []string
|
||||
for _, lg := range logsQuery.LogGroups {
|
||||
arn := lg.ARN
|
||||
arn := lg.Arn
|
||||
// due to a bug in the startQuery api, we remove * from the arn, otherwise it throws an error
|
||||
logGroupIdentifiers = append(logGroupIdentifiers, strings.TrimSuffix(arn, "*"))
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
|
||||
)
|
||||
|
||||
@ -29,7 +30,9 @@ var executeSyncLogQuery = func(ctx context.Context, e *cloudWatchExecutor, req *
|
||||
}
|
||||
|
||||
logsQuery.Subtype = "StartQuery"
|
||||
logsQuery.QueryString = logsQuery.Expression
|
||||
if logsQuery.Expression != nil {
|
||||
logsQuery.QueryString = *logsQuery.Expression
|
||||
}
|
||||
|
||||
region := logsQuery.Region
|
||||
if logsQuery.Region == "" || region == defaultRegion {
|
||||
@ -86,7 +89,9 @@ func (e *cloudWatchExecutor) syncQuery(ctx context.Context, logsClient cloudwatc
|
||||
}
|
||||
|
||||
requestParams := models.LogsQuery{
|
||||
Region: logsQuery.Region,
|
||||
CloudWatchLogsQuery: dataquery.CloudWatchLogsQuery{
|
||||
Region: logsQuery.Region,
|
||||
},
|
||||
QueryId: *startQueryOutput.QueryId,
|
||||
}
|
||||
|
||||
|
@ -17,22 +17,23 @@ import (
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery"
|
||||
)
|
||||
|
||||
type (
|
||||
MetricEditorMode uint32
|
||||
MetricQueryType uint32
|
||||
MetricEditorMode dataquery.MetricEditorMode
|
||||
MetricQueryType dataquery.MetricQueryType
|
||||
GMDApiMode uint32
|
||||
)
|
||||
|
||||
const (
|
||||
MetricEditorModeBuilder MetricEditorMode = iota
|
||||
MetricEditorModeRaw
|
||||
MetricEditorModeBuilder = dataquery.CloudWatchMetricsQueryMetricEditorModeN0
|
||||
MetricEditorModeRaw = dataquery.CloudWatchMetricsQueryMetricEditorModeN1
|
||||
)
|
||||
|
||||
const (
|
||||
MetricQueryTypeSearch MetricQueryType = iota
|
||||
MetricQueryTypeQuery
|
||||
MetricQueryTypeSearch = dataquery.CloudWatchMetricsQueryMetricQueryTypeN0
|
||||
MetricQueryTypeQuery = dataquery.CloudWatchMetricsQueryMetricQueryTypeN1
|
||||
)
|
||||
|
||||
const (
|
||||
@ -67,8 +68,8 @@ type CloudWatchQuery struct {
|
||||
MatchExact bool
|
||||
UsedExpression string
|
||||
TimezoneUTCOffset string
|
||||
MetricQueryType MetricQueryType
|
||||
MetricEditorMode MetricEditorMode
|
||||
MetricQueryType dataquery.CloudWatchMetricsQueryMetricQueryType
|
||||
MetricEditorMode dataquery.CloudWatchMetricsQueryMetricEditorMode
|
||||
AccountId *string
|
||||
}
|
||||
|
||||
@ -213,25 +214,9 @@ const timeSeriesQuery = "timeSeriesQuery"
|
||||
var validMetricDataID = regexp.MustCompile(`^[a-z][a-zA-Z0-9_]*$`)
|
||||
|
||||
type metricsDataQuery struct {
|
||||
Dimensions map[string]interface{} `json:"dimensions"`
|
||||
Expression string `json:"expression"`
|
||||
Label *string `json:"label"`
|
||||
Id string `json:"id"`
|
||||
MatchExact *bool `json:"matchExact"`
|
||||
MetricEditorMode *MetricEditorMode `json:"metricEditorMode"`
|
||||
MetricName string `json:"metricName"`
|
||||
MetricQueryType MetricQueryType `json:"metricQueryType"`
|
||||
Namespace string `json:"namespace"`
|
||||
Period string `json:"period"`
|
||||
Region string `json:"region"`
|
||||
SqlExpression string `json:"sqlExpression"`
|
||||
Statistic *string `json:"statistic"`
|
||||
Statistics []*string `json:"statistics"`
|
||||
TimezoneUTCOffset string `json:"timezoneUTCOffset"`
|
||||
QueryType string `json:"type"`
|
||||
Hide *bool `json:"hide"`
|
||||
Alias string `json:"alias"`
|
||||
AccountId *string `json:"accountId"`
|
||||
dataquery.CloudWatchMetricsQuery
|
||||
Type string `json:"type"`
|
||||
TimezoneUTCOffset string `json:"timezoneUTCOffset"`
|
||||
}
|
||||
|
||||
// ParseMetricDataQueries decodes the metric data queries json, validates, sets default values and returns an array of CloudWatchQueries.
|
||||
@ -246,7 +231,7 @@ func ParseMetricDataQueries(dataQueries []backend.DataQuery, startTime time.Time
|
||||
return nil, &QueryError{Err: err, RefID: query.RefID}
|
||||
}
|
||||
|
||||
queryType := metricsDataQuery.QueryType
|
||||
queryType := metricsDataQuery.Type
|
||||
if queryType != timeSeriesQuery && queryType != "" {
|
||||
continue
|
||||
}
|
||||
@ -255,19 +240,35 @@ func ParseMetricDataQueries(dataQueries []backend.DataQuery, startTime time.Time
|
||||
}
|
||||
|
||||
result := make([]*CloudWatchQuery, 0, len(metricDataQueries))
|
||||
|
||||
for refId, mdq := range metricDataQueries {
|
||||
cwQuery := &CloudWatchQuery{
|
||||
logger: logger,
|
||||
Alias: mdq.Alias,
|
||||
RefId: refId,
|
||||
Id: mdq.Id,
|
||||
Region: mdq.Region,
|
||||
Namespace: mdq.Namespace,
|
||||
MetricName: mdq.MetricName,
|
||||
MetricQueryType: mdq.MetricQueryType,
|
||||
SqlExpression: mdq.SqlExpression,
|
||||
TimezoneUTCOffset: mdq.TimezoneUTCOffset,
|
||||
Expression: mdq.Expression,
|
||||
}
|
||||
|
||||
if mdq.Alias != nil {
|
||||
cwQuery.Alias = *mdq.Alias
|
||||
}
|
||||
|
||||
if mdq.MetricName != nil {
|
||||
cwQuery.MetricName = *mdq.MetricName
|
||||
}
|
||||
|
||||
if mdq.MetricQueryType != nil {
|
||||
cwQuery.MetricQueryType = *mdq.MetricQueryType
|
||||
}
|
||||
|
||||
if mdq.SqlExpression != nil {
|
||||
cwQuery.SqlExpression = *mdq.SqlExpression
|
||||
}
|
||||
|
||||
if mdq.Expression != nil {
|
||||
cwQuery.Expression = *mdq.Expression
|
||||
}
|
||||
|
||||
if err := cwQuery.validateAndSetDefaults(refId, mdq, startTime, endTime, defaultRegion, crossAccountQueryingEnabled); err != nil {
|
||||
@ -337,14 +338,14 @@ func (q *CloudWatchQuery) validateAndSetDefaults(refId string, metricsDataQuery
|
||||
if metricsDataQuery.Hide != nil {
|
||||
q.ReturnData = !*metricsDataQuery.Hide
|
||||
}
|
||||
if metricsDataQuery.QueryType == "" {
|
||||
if metricsDataQuery.Type == "" {
|
||||
// If no type is provided we assume we are called by alerting service, which requires to return data!
|
||||
// Note, this is sort of a hack, but the official Grafana interfaces do not carry the information
|
||||
// who (which service) called the TsdbQueryEndpoint.Query(...) function.
|
||||
q.ReturnData = true
|
||||
}
|
||||
|
||||
if metricsDataQuery.MetricEditorMode == nil && len(metricsDataQuery.Expression) > 0 {
|
||||
if metricsDataQuery.MetricEditorMode == nil && metricsDataQuery.Expression != nil && len(*metricsDataQuery.Expression) > 0 {
|
||||
// this should only ever happen if this is an alerting query that has not yet been migrated in the frontend
|
||||
q.MetricEditorMode = MetricEditorModeRaw
|
||||
} else {
|
||||
@ -369,7 +370,7 @@ func (q *CloudWatchQuery) validateAndSetDefaults(refId string, metricsDataQuery
|
||||
func getStatistic(query metricsDataQuery) string {
|
||||
// If there's not a statistic property in the json, we know it's the legacy format and then it has to be migrated
|
||||
if query.Statistic == nil {
|
||||
return *query.Statistics[0]
|
||||
return query.Statistics[0]
|
||||
}
|
||||
return *query.Statistic
|
||||
}
|
||||
@ -389,14 +390,17 @@ func getLabel(query metricsDataQuery, dynamicLabelsEnabled bool) string {
|
||||
if query.Label != nil {
|
||||
return *query.Label
|
||||
}
|
||||
if query.Alias == "" {
|
||||
if query.Alias != nil && *query.Alias == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
var result string
|
||||
if dynamicLabelsEnabled {
|
||||
fullAliasField := query.Alias
|
||||
matches := legacyAliasRegexp.FindAllStringSubmatch(query.Alias, -1)
|
||||
fullAliasField := ""
|
||||
if query.Alias != nil {
|
||||
fullAliasField = *query.Alias
|
||||
}
|
||||
matches := legacyAliasRegexp.FindAllStringSubmatch(fullAliasField, -1)
|
||||
|
||||
for _, groups := range matches {
|
||||
fullMatch := groups[0]
|
||||
@ -428,7 +432,10 @@ func calculatePeriodBasedOnTimeRange(startTime, endTime time.Time) int {
|
||||
}
|
||||
|
||||
func getPeriod(query metricsDataQuery, startTime, endTime time.Time) (int, error) {
|
||||
periodString := query.Period
|
||||
periodString := ""
|
||||
if query.Period != nil {
|
||||
periodString = *query.Period
|
||||
}
|
||||
var period int
|
||||
var err error
|
||||
if strings.ToLower(periodString) == "auto" || periodString == "" {
|
||||
|
@ -8,10 +8,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/kindsys"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log/logtest"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils"
|
||||
)
|
||||
|
||||
@ -294,7 +296,7 @@ func TestQueryJSON(t *testing.T) {
|
||||
var res metricsDataQuery
|
||||
err := json.Unmarshal(jsonString, &res)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "timeSeriesQuery", res.QueryType)
|
||||
assert.Equal(t, "timeSeriesQuery", res.Type)
|
||||
}
|
||||
|
||||
func TestRequestParser(t *testing.T) {
|
||||
@ -622,11 +624,11 @@ func Test_ParseMetricDataQueries_periods(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_ParseMetricDataQueries_query_type_and_metric_editor_mode_and_GMD_query_api_mode(t *testing.T) {
|
||||
const dummyTestEditorMode MetricEditorMode = 99
|
||||
const dummyTestEditorMode dataquery.CloudWatchMetricsQueryMetricEditorMode = 99
|
||||
testCases := map[string]struct {
|
||||
extraDataQueryJson string
|
||||
expectedMetricQueryType MetricQueryType
|
||||
expectedMetricEditorMode MetricEditorMode
|
||||
expectedMetricQueryType dataquery.CloudWatchMetricsQueryMetricQueryType
|
||||
expectedMetricEditorMode dataquery.CloudWatchMetricsQueryMetricEditorMode
|
||||
expectedGMDApiMode GMDApiMode
|
||||
}{
|
||||
"no metric query type, no metric editor mode, no expression": {
|
||||
@ -930,16 +932,18 @@ func Test_migrateAliasToDynamicLabel_single_query_preserves_old_alias_and_create
|
||||
false := false
|
||||
|
||||
queryToMigrate := metricsDataQuery{
|
||||
Region: "us-east-1",
|
||||
Namespace: "ec2",
|
||||
MetricName: "CPUUtilization",
|
||||
Alias: tc.inputAlias,
|
||||
Dimensions: map[string]interface{}{
|
||||
"InstanceId": []interface{}{"test"},
|
||||
CloudWatchMetricsQuery: dataquery.CloudWatchMetricsQuery{
|
||||
Region: "us-east-1",
|
||||
Namespace: "ec2",
|
||||
MetricName: kindsys.Ptr("CPUUtilization"),
|
||||
Alias: kindsys.Ptr(tc.inputAlias),
|
||||
Dimensions: map[string]interface{}{
|
||||
"InstanceId": []interface{}{"test"},
|
||||
},
|
||||
Statistic: &average,
|
||||
Period: kindsys.Ptr("600"),
|
||||
Hide: &false,
|
||||
},
|
||||
Statistic: &average,
|
||||
Period: "600",
|
||||
Hide: &false,
|
||||
}
|
||||
|
||||
assert.Equal(t, tc.expectedLabel, getLabel(queryToMigrate, true))
|
||||
|
@ -1,28 +1,18 @@
|
||||
package models
|
||||
|
||||
type LogGroup struct {
|
||||
ARN string `json:"arn"`
|
||||
Name string `json:"name"`
|
||||
AccountID string `json:"accountId"`
|
||||
}
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery"
|
||||
)
|
||||
|
||||
type LogsQuery struct {
|
||||
LogType string `json:"type"`
|
||||
SubType string
|
||||
Limit *int64
|
||||
Time int64
|
||||
StartTime *int64
|
||||
EndTime *int64
|
||||
LogGroupName string
|
||||
LogGroupNames []string
|
||||
LogGroups []LogGroup `json:"logGroups"`
|
||||
LogGroupNamePrefix string
|
||||
LogStreamName string
|
||||
StartFromHead bool
|
||||
Region string
|
||||
QueryString string
|
||||
QueryId string
|
||||
StatsGroups []string
|
||||
Subtype string
|
||||
Expression string
|
||||
dataquery.CloudWatchLogsQuery
|
||||
StartTime *int64
|
||||
EndTime *int64
|
||||
Limit *int64
|
||||
LogGroupName string
|
||||
LogStreamName string
|
||||
QueryId string
|
||||
QueryString string
|
||||
StartFromHead bool
|
||||
Subtype string
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
|
||||
|
||||
@ -270,16 +271,16 @@ type queryDimensions struct {
|
||||
}
|
||||
|
||||
type queryParameters struct {
|
||||
MetricQueryType models.MetricQueryType `json:"metricQueryType"`
|
||||
MetricEditorMode models.MetricEditorMode `json:"metricEditorMode"`
|
||||
Dimensions queryDimensions `json:"dimensions"`
|
||||
Expression string `json:"expression"`
|
||||
Alias string `json:"alias"`
|
||||
Label *string `json:"label"`
|
||||
Statistic string `json:"statistic"`
|
||||
Period string `json:"period"`
|
||||
MatchExact bool `json:"matchExact"`
|
||||
MetricName string `json:"metricName"`
|
||||
MetricQueryType dataquery.CloudWatchMetricsQueryMetricQueryType `json:"metricQueryType"`
|
||||
MetricEditorMode dataquery.CloudWatchMetricsQueryMetricEditorMode `json:"metricEditorMode"`
|
||||
Dimensions queryDimensions `json:"dimensions"`
|
||||
Expression string `json:"expression"`
|
||||
Alias string `json:"alias"`
|
||||
Label *string `json:"label"`
|
||||
Statistic string `json:"statistic"`
|
||||
Period string `json:"period"`
|
||||
MatchExact bool `json:"matchExact"`
|
||||
MetricName string `json:"metricName"`
|
||||
}
|
||||
|
||||
var queryId = "query id"
|
||||
@ -288,11 +289,11 @@ func newTestQuery(t testing.TB, p queryParameters) json.RawMessage {
|
||||
t.Helper()
|
||||
|
||||
tsq := struct {
|
||||
Type string `json:"type"`
|
||||
MetricQueryType models.MetricQueryType `json:"metricQueryType"`
|
||||
MetricEditorMode models.MetricEditorMode `json:"metricEditorMode"`
|
||||
Namespace string `json:"namespace"`
|
||||
MetricName string `json:"metricName"`
|
||||
Type string `json:"type"`
|
||||
MetricQueryType dataquery.CloudWatchMetricsQueryMetricQueryType `json:"metricQueryType"`
|
||||
MetricEditorMode dataquery.CloudWatchMetricsQueryMetricEditorMode `json:"metricEditorMode"`
|
||||
Namespace string `json:"namespace"`
|
||||
MetricName string `json:"metricName"`
|
||||
Dimensions struct {
|
||||
InstanceID []string `json:"InstanceId,omitempty"`
|
||||
} `json:"dimensions"`
|
||||
|
@ -23,7 +23,7 @@ import (
|
||||
pfs.GrafanaPlugin
|
||||
|
||||
composableKinds: DataQuery: {
|
||||
maturity: "merged"
|
||||
maturity: "experimental"
|
||||
|
||||
lineage: {
|
||||
seqs: [
|
||||
|
Loading…
Reference in New Issue
Block a user