From 6edefe51478f19caa53464ddce955f0227cc6f17 Mon Sep 17 00:00:00 2001 From: Andres Martinez Gotor Date: Thu, 28 Apr 2022 01:27:39 -0700 Subject: [PATCH] AzureMonitor: Remove deprecated code (#48328) --- .betterer.results | 3 - .../azuremonitor-resource-handler.go | 3 - pkg/tsdb/azuremonitor/azuremonitor.go | 9 - pkg/tsdb/azuremonitor/azuremonitor_test.go | 30 -- .../applicationinsights-datasource.go | 269 ---------- .../applicationinsights-datasource_test.go | 237 --------- .../deprecated/applicationinsights-metrics.go | 314 ------------ .../applicationinsights-metrics_test.go | 192 -------- .../azuremonitor/deprecated/httpclient.go | 20 - .../insights-analytics-datasource.go | 190 ------- .../insights-analytics-datasource_test.go | 45 -- pkg/tsdb/azuremonitor/deprecated/routes.go | 23 - pkg/tsdb/azuremonitor/deprecated/types.go | 72 --- pkg/tsdb/azuremonitor/httpclient.go | 20 +- pkg/tsdb/azuremonitor/httpclient_test.go | 53 -- pkg/tsdb/azuremonitor/routes.go | 17 +- .../__mocks__/query.ts | 7 - .../components/ConfigEditor.test.tsx | 16 - .../components/ConfigEditor.tsx | 11 - .../QueryEditor/QueryEditor.test.tsx | 67 +-- .../components/QueryEditor/QueryEditor.tsx | 64 +-- .../components/QueryEditor/QueryTypeField.tsx | 24 +- .../app_insights_datasource.test.ts | 463 ------------------ .../app_insights/app_insights_datasource.ts | 171 ------- .../app_insights/response_parser.ts | 236 --------- .../components/AnalyticsConfig.test.tsx | 99 ---- .../deprecated/components/AnalyticsConfig.tsx | 64 --- .../ApplicationInsightsEditor/index.tsx | 73 --- .../InsightsAnalyticsEditor/index.tsx | 52 -- .../components/InsightsConfig.test.tsx | 110 ----- .../deprecated/components/InsightsConfig.tsx | 98 ---- .../InsightsConfig.test.tsx.snap | 208 -------- .../insights_analytics_datasource.ts | 30 -- .../components/deprecated/types/index.ts | 1 - .../components/deprecated/types/query.ts | 61 --- .../components/deprecated/utils.tsx | 14 - .../datasource.ts | 57 +-- .../types/query.ts | 12 +- .../utils/migrateQuery.test.ts | 2 - .../utils/migrateQuery.ts | 58 +-- .../variables.test.ts | 56 --- .../variables.ts | 11 - 42 files changed, 39 insertions(+), 3523 deletions(-) delete mode 100644 pkg/tsdb/azuremonitor/deprecated/applicationinsights-datasource.go delete mode 100644 pkg/tsdb/azuremonitor/deprecated/applicationinsights-datasource_test.go delete mode 100644 pkg/tsdb/azuremonitor/deprecated/applicationinsights-metrics.go delete mode 100644 pkg/tsdb/azuremonitor/deprecated/applicationinsights-metrics_test.go delete mode 100644 pkg/tsdb/azuremonitor/deprecated/httpclient.go delete mode 100644 pkg/tsdb/azuremonitor/deprecated/insights-analytics-datasource.go delete mode 100644 pkg/tsdb/azuremonitor/deprecated/insights-analytics-datasource_test.go delete mode 100644 pkg/tsdb/azuremonitor/deprecated/routes.go delete mode 100644 pkg/tsdb/azuremonitor/deprecated/types.go delete mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/app_insights/app_insights_datasource.test.ts delete mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/app_insights/app_insights_datasource.ts delete mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/app_insights/response_parser.ts delete mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/AnalyticsConfig.test.tsx delete mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/AnalyticsConfig.tsx delete mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/ApplicationInsightsEditor/index.tsx delete mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/InsightsAnalyticsEditor/index.tsx delete mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/InsightsConfig.test.tsx delete mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/InsightsConfig.tsx delete mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/__snapshots__/InsightsConfig.test.tsx.snap delete mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/insights_analytics/insights_analytics_datasource.ts delete mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/types/index.ts delete mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/types/query.ts delete mode 100644 public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/utils.tsx diff --git a/.betterer.results b/.betterer.results index ab7564f8543..1586689c545 100644 --- a/.betterer.results +++ b/.betterer.results @@ -299,9 +299,6 @@ exports[`no enzyme tests`] = { "public/app/plugins/datasource/elasticsearch/configuration/DataLinks.test.tsx:248699332": [ [0, 17, 13, "RegExp match", "2409514259"] ], - "public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/InsightsConfig.test.tsx:1107283234": [ - [0, 19, 13, "RegExp match", "2409514259"] - ], "public/app/plugins/datasource/influxdb/components/ConfigEditor.test.tsx:3456478975": [ [0, 19, 13, "RegExp match", "2409514259"] ], diff --git a/pkg/tsdb/azuremonitor/azuremonitor-resource-handler.go b/pkg/tsdb/azuremonitor/azuremonitor-resource-handler.go index a07295868e5..82e148c5984 100644 --- a/pkg/tsdb/azuremonitor/azuremonitor-resource-handler.go +++ b/pkg/tsdb/azuremonitor/azuremonitor-resource-handler.go @@ -9,7 +9,6 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/azlog" - "github.com/grafana/grafana/pkg/tsdb/azuremonitor/deprecated" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" ) @@ -125,7 +124,5 @@ func (s *Service) newResourceMux() *http.ServeMux { mux.HandleFunc("/azuremonitor/", s.handleResourceReq(azureMonitor)) mux.HandleFunc("/loganalytics/", s.handleResourceReq(azureLogAnalytics)) mux.HandleFunc("/resourcegraph/", s.handleResourceReq(azureResourceGraph)) - // Remove with Grafana 9 - mux.HandleFunc("/appinsights/", s.handleResourceReq(deprecated.AppInsights)) return mux } diff --git a/pkg/tsdb/azuremonitor/azuremonitor.go b/pkg/tsdb/azuremonitor/azuremonitor.go index 2ef013fab7d..509ae8d6433 100644 --- a/pkg/tsdb/azuremonitor/azuremonitor.go +++ b/pkg/tsdb/azuremonitor/azuremonitor.go @@ -6,7 +6,6 @@ import ( "fmt" "net/http" - "github.com/Masterminds/semver" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" @@ -15,7 +14,6 @@ import ( "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/setting" - "github.com/grafana/grafana/pkg/tsdb/azuremonitor/deprecated" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/loganalytics" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/metrics" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/resourcegraph" @@ -30,13 +28,6 @@ func ProvideService(cfg *setting.Cfg, httpClientProvider *httpclient.Provider, t azureResourceGraph: &resourcegraph.AzureResourceGraphDatasource{Proxy: proxy}, } - // Insights Analytics and Application Insights were deprecated in Grafana 8.x and - // will be finally removed with Grafana 9 - if setting.BuildVersion != "" && semver.MustParse(setting.BuildVersion).Compare(semver.MustParse("9.0.0-beta1")) < 0 { - executors[deprecated.InsightsAnalytics] = &deprecated.InsightsAnalyticsDatasource{Proxy: proxy} - executors[deprecated.AppInsights] = &deprecated.ApplicationInsightsDatasource{Proxy: proxy} - } - im := datasource.NewInstanceManager(NewInstanceSettings(cfg, httpClientProvider, executors)) s := &Service{ diff --git a/pkg/tsdb/azuremonitor/azuremonitor_test.go b/pkg/tsdb/azuremonitor/azuremonitor_test.go index 0e5d1f99017..bad21ca99fc 100644 --- a/pkg/tsdb/azuremonitor/azuremonitor_test.go +++ b/pkg/tsdb/azuremonitor/azuremonitor_test.go @@ -14,41 +14,11 @@ import ( "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/setting" - "github.com/grafana/grafana/pkg/tsdb/azuremonitor/deprecated" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestProvideService(t *testing.T) { - t.Run("it should skip insight analytics and app insights with Grafana 9", func(t *testing.T) { - currentV := setting.BuildVersion - t.Cleanup(func() { - setting.BuildVersion = currentV - }) - versions := []struct { - version string - shouldIncludeInsights bool - }{ - {"8.5.0", true}, - {"9.0.0-beta1", false}, - {"9.0.0", false}, - } - for _, v := range versions { - setting.BuildVersion = v.version - s := ProvideService(setting.NewCfg(), httpclient.NewProvider(), nil) - if v.shouldIncludeInsights { - assert.NotNil(t, s.executors[deprecated.InsightsAnalytics]) - assert.NotNil(t, s.executors[deprecated.AppInsights]) - } else { - assert.Nil(t, s.executors[deprecated.InsightsAnalytics]) - assert.Nil(t, s.executors[deprecated.AppInsights]) - } - } - }) -} - func TestNewInstanceSettings(t *testing.T) { tests := []struct { name string diff --git a/pkg/tsdb/azuremonitor/deprecated/applicationinsights-datasource.go b/pkg/tsdb/azuremonitor/deprecated/applicationinsights-datasource.go deleted file mode 100644 index 4520522531e..00000000000 --- a/pkg/tsdb/azuremonitor/deprecated/applicationinsights-datasource.go +++ /dev/null @@ -1,269 +0,0 @@ -package deprecated - -import ( - "context" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "path" - "sort" - "strings" - "time" - - "github.com/grafana/grafana-plugin-sdk-go/backend" - "github.com/grafana/grafana-plugin-sdk-go/data" - "go.opentelemetry.io/otel/attribute" - - "github.com/grafana/grafana/pkg/infra/tracing" - "github.com/grafana/grafana/pkg/tsdb/azuremonitor/azlog" - azTime "github.com/grafana/grafana/pkg/tsdb/azuremonitor/time" - "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" - "github.com/grafana/grafana/pkg/util/errutil" -) - -// ApplicationInsightsDatasource calls the application insights query API. -type ApplicationInsightsDatasource struct { - Proxy types.ServiceProxy -} - -// ApplicationInsightsQuery is the model that holds the information -// needed to make a metrics query to Application Insights, and the information -// used to parse the response. -type ApplicationInsightsQuery struct { - RefID string - TimeRange backend.TimeRange - - // Text based raw query options. - ApiURL string - Params url.Values - Alias string - Target string - - // These fields are used when parsing the response. - metricName string - dimensions []string - aggregation string -} - -func (e *ApplicationInsightsDatasource) ResourceRequest(rw http.ResponseWriter, req *http.Request, cli *http.Client) { - e.Proxy.Do(rw, req, cli) -} - -func (e *ApplicationInsightsDatasource) ExecuteTimeSeriesQuery(ctx context.Context, - originalQueries []backend.DataQuery, dsInfo types.DatasourceInfo, client *http.Client, - url string, tracer tracing.Tracer) (*backend.QueryDataResponse, error) { - result := backend.NewQueryDataResponse() - - queries, err := e.buildQueries(originalQueries) - if err != nil { - return nil, err - } - - for _, query := range queries { - queryRes, err := e.executeQuery(ctx, query, dsInfo, client, url, tracer) - if err != nil { - return nil, err - } - result.Responses[query.RefID] = queryRes - } - - return result, nil -} - -func (e *ApplicationInsightsDatasource) buildQueries(queries []backend.DataQuery) ([]*ApplicationInsightsQuery, error) { - applicationInsightsQueries := []*ApplicationInsightsQuery{} - - for _, query := range queries { - queryBytes, err := query.JSON.MarshalJSON() - if err != nil { - return nil, fmt.Errorf("failed to re-encode the Azure Application Insights query into JSON: %w", err) - } - queryJSONModel := insightsJSONQuery{} - err = json.Unmarshal(queryBytes, &queryJSONModel) - if err != nil { - return nil, fmt.Errorf("failed to decode the Azure Application Insights query object from JSON: %w", err) - } - - insightsJSONModel := queryJSONModel.AppInsights - azlog.Debug("Application Insights", "target", insightsJSONModel) - - azureURL := fmt.Sprintf("metrics/%s", insightsJSONModel.MetricName) - timeGrain := insightsJSONModel.TimeGrain - timeGrains := insightsJSONModel.AllowedTimeGrainsMs - - // Previous versions of the query model don't specify a time grain, so we - // need to fallback to a default value - if timeGrain == "auto" || timeGrain == "" { - timeGrain, err = azTime.SetAutoTimeGrain(query.Interval.Milliseconds(), timeGrains) - if err != nil { - return nil, err - } - } - - params := url.Values{} - params.Add("timespan", fmt.Sprintf("%v/%v", query.TimeRange.From.UTC().Format(time.RFC3339), query.TimeRange.To.UTC().Format(time.RFC3339))) - if timeGrain != "none" { - params.Add("interval", timeGrain) - } - params.Add("aggregation", insightsJSONModel.Aggregation) - - dimensionFilter := strings.TrimSpace(insightsJSONModel.DimensionFilter) - if dimensionFilter != "" { - params.Add("filter", dimensionFilter) - } - - if len(insightsJSONModel.Dimensions) != 0 { - params.Add("segment", strings.Join(insightsJSONModel.Dimensions, ",")) - } - applicationInsightsQueries = append(applicationInsightsQueries, &ApplicationInsightsQuery{ - RefID: query.RefID, - TimeRange: query.TimeRange, - ApiURL: azureURL, - Params: params, - Alias: insightsJSONModel.Alias, - Target: params.Encode(), - metricName: insightsJSONModel.MetricName, - aggregation: insightsJSONModel.Aggregation, - dimensions: insightsJSONModel.Dimensions, - }) - } - - return applicationInsightsQueries, nil -} - -func (e *ApplicationInsightsDatasource) executeQuery(ctx context.Context, query *ApplicationInsightsQuery, dsInfo types.DatasourceInfo, client *http.Client, url string, tracer tracing.Tracer) ( - backend.DataResponse, error) { - dataResponse := backend.DataResponse{} - - req, err := e.createRequest(ctx, dsInfo, url) - if err != nil { - dataResponse.Error = err - return dataResponse, nil - } - - req.URL.Path = path.Join(req.URL.Path, query.ApiURL) - req.URL.RawQuery = query.Params.Encode() - - ctx, span := tracer.Start(ctx, "application insights query") - span.SetAttributes("target", query.Target, attribute.Key("target").String(query.Target)) - span.SetAttributes("from", query.TimeRange.From.UnixNano()/int64(time.Millisecond), attribute.Key("from").Int64(query.TimeRange.From.UnixNano()/int64(time.Millisecond))) - span.SetAttributes("until", query.TimeRange.To.UnixNano()/int64(time.Millisecond), attribute.Key("until").Int64(query.TimeRange.To.UnixNano()/int64(time.Millisecond))) - span.SetAttributes("datasource_id", dsInfo.DatasourceID, attribute.Key("datasource_id").Int64(dsInfo.DatasourceID)) - span.SetAttributes("org_id", dsInfo.OrgID, attribute.Key("org_id").Int64(dsInfo.OrgID)) - - defer span.End() - - tracer.Inject(ctx, req.Header, span) - - azlog.Debug("ApplicationInsights", "Request URL", req.URL.String()) - res, err := client.Do(req) - if err != nil { - dataResponse.Error = err - return dataResponse, nil - } - - body, err := ioutil.ReadAll(res.Body) - defer func() { - if err := res.Body.Close(); err != nil { - azlog.Warn("Failed to close response body", "err", err) - } - }() - if err != nil { - return backend.DataResponse{}, err - } - - if res.StatusCode/100 != 2 { - azlog.Debug("Request failed", "status", res.Status, "body", string(body)) - return backend.DataResponse{}, fmt.Errorf("request failed, status: %s", res.Status) - } - - mr := MetricsResult{} - err = json.Unmarshal(body, &mr) - if err != nil { - return backend.DataResponse{}, err - } - - frame, err := InsightsMetricsResultToFrame(mr, query.metricName, query.aggregation, query.dimensions) - if err != nil { - dataResponse.Error = err - return dataResponse, nil - } - - applyInsightsMetricAlias(frame, query.Alias) - - dataResponse.Frames = data.Frames{frame} - return dataResponse, nil -} - -func (e *ApplicationInsightsDatasource) createRequest(ctx context.Context, dsInfo types.DatasourceInfo, url string) (*http.Request, error) { - appInsightsAppID := dsInfo.Settings.AppInsightsAppId - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - azlog.Debug("Failed to create request", "error", err) - return nil, errutil.Wrap("Failed to create request", err) - } - req.URL.Path = fmt.Sprintf("/v1/apps/%s", appInsightsAppID) - - return req, nil -} - -// formatApplicationInsightsLegendKey builds the legend key or timeseries name -// Alias patterns like {{metric}} are replaced with the appropriate data values. -func formatApplicationInsightsLegendKey(alias string, metricName string, labels data.Labels) string { - // Could be a collision problem if there were two keys that varied only in case, but I don't think that would happen in azure. - lowerLabels := data.Labels{} - for k, v := range labels { - lowerLabels[strings.ToLower(k)] = v - } - keys := make([]string, 0, len(labels)) - for k := range lowerLabels { - keys = append(keys, k) - } - keys = sort.StringSlice(keys) - - result := types.LegendKeyFormat.ReplaceAllFunc([]byte(alias), func(in []byte) []byte { - metaPartName := strings.Replace(string(in), "{{", "", 1) - metaPartName = strings.Replace(metaPartName, "}}", "", 1) - metaPartName = strings.ToLower(strings.TrimSpace(metaPartName)) - - switch metaPartName { - case "metric": - return []byte(metricName) - case "dimensionname", "groupbyname": - return []byte(keys[0]) - case "dimensionvalue", "groupbyvalue": - return []byte(lowerLabels[keys[0]]) - } - - if v, ok := lowerLabels[metaPartName]; ok { - return []byte(v) - } - - return in - }) - - return string(result) -} - -func applyInsightsMetricAlias(frame *data.Frame, alias string) { - if alias == "" { - return - } - - for _, field := range frame.Fields { - if field.Type() == data.FieldTypeTime || field.Type() == data.FieldTypeNullableTime { - continue - } - - displayName := formatApplicationInsightsLegendKey(alias, field.Name, field.Labels) - - if field.Config == nil { - field.Config = &data.FieldConfig{} - } - - field.Config.DisplayName = displayName - } -} diff --git a/pkg/tsdb/azuremonitor/deprecated/applicationinsights-datasource_test.go b/pkg/tsdb/azuremonitor/deprecated/applicationinsights-datasource_test.go deleted file mode 100644 index 6728c6b721e..00000000000 --- a/pkg/tsdb/azuremonitor/deprecated/applicationinsights-datasource_test.go +++ /dev/null @@ -1,237 +0,0 @@ -package deprecated - -import ( - "context" - "encoding/json" - "testing" - "time" - - "github.com/grafana/grafana-plugin-sdk-go/backend" - "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" - "github.com/stretchr/testify/require" -) - -func TestApplicationInsightsDatasource(t *testing.T) { - t.Run("ApplicationInsightsDatasource", func(t *testing.T) { - datasource := &ApplicationInsightsDatasource{} - - t.Run("Parse queries from frontend and build AzureMonitor API queries", func(t *testing.T) { - fromStart := time.Date(2018, 3, 15, 13, 0, 0, 0, time.UTC).In(time.Local) - tsdbQuery := []backend.DataQuery{ - { - TimeRange: backend.TimeRange{ - From: fromStart, - To: fromStart.Add(34 * time.Minute), - }, - JSON: []byte(`{ - "appInsights": { - "rawQuery": false, - "timeGrain": "PT1M", - "aggregation": "Average", - "metricName": "server/exceptions", - "alias": "testalias", - "queryType": "Application Insights" - } - }`), - RefID: "A", - Interval: 1234, - }, - } - t.Run("and is a normal query", func(t *testing.T) { - queries, err := datasource.buildQueries(tsdbQuery) - require.NoError(t, err) - - require.Equal(t, len(queries), 1) - require.Equal(t, queries[0].RefID, "A") - require.Equal(t, queries[0].ApiURL, "metrics/server/exceptions") - require.Equal(t, queries[0].Target, "aggregation=Average&interval=PT1M×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z") - require.Equal(t, len(queries[0].Params), 3) - require.Equal(t, queries[0].Params["timespan"][0], "2018-03-15T13:00:00Z/2018-03-15T13:34:00Z") - require.Equal(t, queries[0].Params["aggregation"][0], "Average") - require.Equal(t, queries[0].Params["interval"][0], "PT1M") - require.Equal(t, queries[0].Alias, "testalias") - }) - - t.Run("and has a time grain set to auto", func(t *testing.T) { - tsdbQuery[0].JSON = []byte(`{ - "appInsights": { - "rawQuery": false, - "timeGrain": "auto", - "aggregation": "Average", - "metricName": "Percentage CPU", - "alias": "testalias", - "queryType": "Application Insights" - } - }`) - var err error - tsdbQuery[0].Interval, err = time.ParseDuration("400s") - require.NoError(t, err) - - queries, err := datasource.buildQueries(tsdbQuery) - require.NoError(t, err) - - require.Equal(t, queries[0].Params["interval"][0], "PT15M") - }) - - t.Run("and has an empty time grain", func(t *testing.T) { - tsdbQuery[0].JSON = []byte(`{ - "appInsights": { - "rawQuery": false, - "timeGrain": "", - "aggregation": "Average", - "metricName": "Percentage CPU", - "alias": "testalias", - "queryType": "Application Insights" - } - }`) - tsdbQuery[0].Interval, _ = time.ParseDuration("400s") - - queries, err := datasource.buildQueries(tsdbQuery) - require.NoError(t, err) - - require.Equal(t, queries[0].Params["interval"][0], "PT15M") - }) - - t.Run("and has a time grain set to auto and the metric has a limited list of allowed time grains", func(t *testing.T) { - tsdbQuery[0].JSON = []byte(`{ - "appInsights": { - "rawQuery": false, - "timeGrain": "auto", - "aggregation": "Average", - "metricName": "Percentage CPU", - "alias": "testalias", - "queryType": "Application Insights", - "allowedTimeGrainsMs": [60000, 300000] - } - }`) - tsdbQuery[0].Interval, _ = time.ParseDuration("400s") - - queries, err := datasource.buildQueries(tsdbQuery) - require.NoError(t, err) - - require.Equal(t, queries[0].Params["interval"][0], "PT5M") - }) - - t.Run("and has a dimension filter", func(t *testing.T) { - tsdbQuery[0].JSON = []byte(`{ - "appInsights": { - "rawQuery": false, - "timeGrain": "PT1M", - "aggregation": "Average", - "metricName": "Percentage CPU", - "alias": "testalias", - "queryType": "Application Insights", - "dimension": "blob", - "dimensionFilter": "blob eq '*'" - } - }`) - - queries, err := datasource.buildQueries(tsdbQuery) - require.NoError(t, err) - - require.Equal(t, queries[0].Target, "aggregation=Average&filter=blob+eq+%27%2A%27&interval=PT1M&segment=blob×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z") - require.Equal(t, queries[0].Params["filter"][0], "blob eq '*'") - }) - - t.Run("and has a dimension filter set to None", func(t *testing.T) { - tsdbQuery[0].JSON = []byte(`{ - "appInsights": { - "rawQuery": false, - "timeGrain": "PT1M", - "aggregation": "Average", - "metricName": "Percentage CPU", - "alias": "testalias", - "queryType": "Application Insights", - "dimension": "None" - } - }`) - - queries, err := datasource.buildQueries(tsdbQuery) - require.NoError(t, err) - - require.Equal(t, queries[0].Target, "aggregation=Average&interval=PT1M×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z") - }) - }) - }) -} - -func TestInsightsDimensionsUnmarshalJSON(t *testing.T) { - a := []byte(`"foo"`) - b := []byte(`["foo"]`) - c := []byte(`["none"]`) - d := []byte(`["None"]`) - e := []byte("null") - f := []byte(`""`) - g := []byte(`"none"`) - - var as InsightsDimensions - var bs InsightsDimensions - err := json.Unmarshal(a, &as) - - require.NoError(t, err) - require.Equal(t, []string{"foo"}, []string(as)) - - err = json.Unmarshal(b, &bs) - require.NoError(t, err) - - require.Equal(t, []string{"foo"}, []string(bs)) - - var cs InsightsDimensions - err = json.Unmarshal(c, &cs) - require.NoError(t, err) - require.Empty(t, cs) - - var ds InsightsDimensions - err = json.Unmarshal(d, &ds) - require.NoError(t, err) - require.Empty(t, ds) - - var es InsightsDimensions - err = json.Unmarshal(e, &es) - require.NoError(t, err) - require.Empty(t, es) - - var fs InsightsDimensions - err = json.Unmarshal(f, &fs) - require.NoError(t, err) - require.Empty(t, fs) - - var gs InsightsDimensions - err = json.Unmarshal(g, &gs) - require.NoError(t, err) - require.Empty(t, gs) -} - -func TestAppInsightsCreateRequest(t *testing.T) { - ctx := context.Background() - url := "http://ds" - dsInfo := types.DatasourceInfo{ - Settings: types.AzureMonitorSettings{AppInsightsAppId: "foo"}, - DecryptedSecureJSONData: map[string]string{ - "appInsightsApiKey": "key", - }, - } - - tests := []struct { - name string - expectedURL string - Err require.ErrorAssertionFunc - }{ - { - name: "creates a request", - expectedURL: "http://ds/v1/apps/foo", - Err: require.NoError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ds := ApplicationInsightsDatasource{} - req, err := ds.createRequest(ctx, dsInfo, url) - tt.Err(t, err) - if req.URL.String() != tt.expectedURL { - t.Errorf("Expecting %s, got %s", tt.expectedURL, req.URL.String()) - } - }) - } -} diff --git a/pkg/tsdb/azuremonitor/deprecated/applicationinsights-metrics.go b/pkg/tsdb/azuremonitor/deprecated/applicationinsights-metrics.go deleted file mode 100644 index 316151ab643..00000000000 --- a/pkg/tsdb/azuremonitor/deprecated/applicationinsights-metrics.go +++ /dev/null @@ -1,314 +0,0 @@ -package deprecated - -import ( - "encoding/json" - "fmt" - "time" - - "github.com/grafana/grafana-plugin-sdk-go/data" -) - -// InsightsMetricsResultToFrame converts a MetricsResult (an Application Insights metrics query response) to a dataframe. -// Due to the dynamic nature of the MetricsResult object, the name of the metric, aggregation, -// and requested dimensions are used to determine the expected shape of the object. -// This builds all series into a single data.Frame with one time index (a wide formatted time series frame). -func InsightsMetricsResultToFrame(mr MetricsResult, metric, agg string, dimensions []string) (*data.Frame, error) { - dimLen := len(dimensions) - - // The Response has both Start and End times, so we name the column "StartTime". - frame := data.NewFrame("", data.NewField("StartTime", nil, []time.Time{})) - - fieldIdxMap := map[string]int{} // a map of a string representation of the labels to the Field index in the frame. - - rowCounter := 0 // row in the resulting frame - - if mr.Value == nil { // never seen this response, but to ensure there is no panic - return nil, fmt.Errorf("unexpected nil response or response value in metrics result") - } - - for _, seg := range *mr.Value.Segments { // each top level segment in the response shares timestamps. - frame.Extend(1) - frame.Set(0, rowCounter, seg.Start) // field 0 is the time field - labels := data.Labels{} - - // handleLeafSegment is for the leaf MetricsSegmentInfo nodes in the response. - // A leaf node contains an aggregated value, and when there are multiple dimensions, a label key/value pair. - handleLeafSegment := func(s MetricsSegmentInfo) error { - // since this is a dynamic response, everything we are interested in here from JSON - // is Marshalled (mapped) into the AdditionalProperties property. - v, err := valFromLeafAP(s.AdditionalProperties, metric, agg) - if err != nil { - return err - } - - if dimLen != 0 { // when there are dimensions, the final dimension is in this inner segment. - dimension := dimensions[dimLen-1] - dimVal, err := dimValueFromAP(s.AdditionalProperties, dimension) - if err != nil { - return err - } - labels[dimension] = dimVal - } - - if _, ok := fieldIdxMap[labels.String()]; !ok { - // When we find a new combination of labels for the metric, a new Field is appended. - frame.Fields = append(frame.Fields, data.NewField(metric, labels.Copy(), make([]*float64, rowCounter+1))) - fieldIdxMap[labels.String()] = len(frame.Fields) - 1 - } - - frame.Set(fieldIdxMap[labels.String()], rowCounter, v) - - return nil - } - - // Simple case with no segments/dimensions - if dimLen == 0 { - if err := handleLeafSegment(seg); err != nil { - return nil, err - } - rowCounter++ - continue - } - - // Multiple dimension case - var traverse func(segments *[]MetricsSegmentInfo, depth int) error - - // traverse walks segments collecting dimensions into labels until leaf segments are - // reached, and then handleInnerSegment is called. The final k/v label pair is - // in the leaf segment. - // A non-recursive implementation would probably be better. - traverse = func(segments *[]MetricsSegmentInfo, depth int) error { - if segments == nil { - return nil - } - for _, seg := range *segments { - if seg.Segments == nil { - if err := handleLeafSegment(seg); err != nil { - return err - } - continue - } - dimension := dimensions[depth] - dimVal, err := dimValueFromAP(seg.AdditionalProperties, dimension) - if err != nil { - return err - } - labels[dimension] = dimVal - if err := traverse(seg.Segments, depth+1); err != nil { - return err - } - } - return nil - } - - if err := traverse(seg.Segments, 0); err != nil { - return nil, err - } - rowCounter++ - } - - if len(frame.Fields) == 1 { // No data, only a time column, no sort - return frame, nil - } - - if err := data.SortWideFrameFields(frame, dimensions...); err != nil { - return nil, err - } - - return frame, nil -} - -// valFromLeafAP extracts value for the given metric and aggregation (agg) -// from the dynamic AdditionalProperties properties of a leaf node. It is for use in the InsightsMetricsResultToFrame -// function. -func valFromLeafAP(ap map[string]interface{}, metric, agg string) (*float64, error) { - if ap == nil { - return nil, fmt.Errorf("expected additional properties for metric %v not found in leaf segment", metric) - } - met, ok := ap[metric] - if !ok { - return nil, fmt.Errorf("expected additional properties for metric %v not found in leaf segment", metric) - } - - metMap, ok := met.(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("unexpected type for additional properties not found in leaf segment, want map[string]interface{}, but got %T", met) - } - metVal, ok := metMap[agg] - if !ok { - return nil, fmt.Errorf("expected value for aggregation %v not found in leaf segment", agg) - } - var v *float64 - if val, ok := metVal.(float64); ok { - v = &val - } - - return v, nil -} - -// dimValueFromAP fetches the value as a string for the corresponding dimension from the dynamic AdditionalProperties properties of a leaf node. It is for use in the InsightsMetricsResultToFrame -// function. -func dimValueFromAP(ap map[string]interface{}, dimension string) (string, error) { - rawDimValue, ok := ap[dimension] - if !ok { - return "", fmt.Errorf("expected dimension key %v not found in response", dimension) - } - dimValue, ok := rawDimValue.(string) - if !ok { - return "", fmt.Errorf("unexpected non-string value for the value for dimension %v, got type %T with a value of %v", dimension, rawDimValue, dimValue) - } - return dimValue, nil -} - -// MetricsResult a metric result. -// This is copied from azure-sdk-for-go/services/preview/appinsights/v1/insights. -type MetricsResult struct { - Value *MetricsResultInfo `json:"value,omitempty"` -} - -// MetricsResultInfo a metric result data. -// This is copied from azure-sdk-for-go/services/preview/appinsights/v1/insights (except time Type is changed). -type MetricsResultInfo struct { - // AdditionalProperties - Unmatched properties from the message are deserialized this collection - AdditionalProperties map[string]interface{} `json:""` - // Start - Start time of the metric. - Start time.Time `json:"start,omitempty"` - // End - Start time of the metric. - End time.Time `json:"end,omitempty"` - // Interval - The interval used to segment the metric data. - Interval *string `json:"interval,omitempty"` - // Segments - Segmented metric data (if segmented). - Segments *[]MetricsSegmentInfo `json:"segments,omitempty"` -} - -// MetricsSegmentInfo is a metric segment. -// This is copied from azure-sdk-for-go/services/preview/appinsights/v1/insights (except time Type is changed). -type MetricsSegmentInfo struct { - // AdditionalProperties - Unmatched properties from the message are deserialized this collection - AdditionalProperties map[string]interface{} `json:""` - // Start - Start time of the metric segment (only when an interval was specified). - Start time.Time `json:"start,omitempty"` - // End - Start time of the metric segment (only when an interval was specified). - End time.Time `json:"end,omitempty"` - // Segments - Segmented metric data (if further segmented). - Segments *[]MetricsSegmentInfo `json:"segments,omitempty"` -} - -// UnmarshalJSON is the custom unmarshaler for MetricsSegmentInfo struct. -// This is copied from azure-sdk-for-go/services/preview/appinsights/v1/insights (except time Type is changed). -func (mri *MetricsSegmentInfo) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - default: - if v != nil { - var additionalProperties interface{} - err = json.Unmarshal(*v, &additionalProperties) - if err != nil { - return err - } - if mri.AdditionalProperties == nil { - mri.AdditionalProperties = make(map[string]interface{}) - } - mri.AdditionalProperties[k] = additionalProperties - } - case "start": - if v != nil { - var start time.Time - err = json.Unmarshal(*v, &start) - if err != nil { - return err - } - mri.Start = start - } - case "end": - if v != nil { - var end time.Time - err = json.Unmarshal(*v, &end) - if err != nil { - return err - } - mri.End = end - } - case "segments": - if v != nil { - var segments []MetricsSegmentInfo - err = json.Unmarshal(*v, &segments) - if err != nil { - return err - } - mri.Segments = &segments - } - } - } - - return nil -} - -// UnmarshalJSON is the custom unmarshaler for MetricsResultInfo struct. -// This is copied from azure-sdk-for-go/services/preview/appinsights/v1/insights (except time Type is changed). -func (mri *MetricsResultInfo) UnmarshalJSON(body []byte) error { - var m map[string]*json.RawMessage - err := json.Unmarshal(body, &m) - if err != nil { - return err - } - for k, v := range m { - switch k { - default: - if v != nil { - var additionalProperties interface{} - err = json.Unmarshal(*v, &additionalProperties) - if err != nil { - return err - } - if mri.AdditionalProperties == nil { - mri.AdditionalProperties = make(map[string]interface{}) - } - mri.AdditionalProperties[k] = additionalProperties - } - case "start": - if v != nil { - var start time.Time - err = json.Unmarshal(*v, &start) - if err != nil { - return err - } - mri.Start = start - } - case "end": - if v != nil { - var end time.Time - err = json.Unmarshal(*v, &end) - if err != nil { - return err - } - mri.End = end - } - case "interval": - if v != nil { - var interval string - err = json.Unmarshal(*v, &interval) - if err != nil { - return err - } - mri.Interval = &interval - } - case "segments": - if v != nil { - var segments []MetricsSegmentInfo - err = json.Unmarshal(*v, &segments) - if err != nil { - return err - } - mri.Segments = &segments - } - } - } - - return nil -} diff --git a/pkg/tsdb/azuremonitor/deprecated/applicationinsights-metrics_test.go b/pkg/tsdb/azuremonitor/deprecated/applicationinsights-metrics_test.go deleted file mode 100644 index a56c2bff4c3..00000000000 --- a/pkg/tsdb/azuremonitor/deprecated/applicationinsights-metrics_test.go +++ /dev/null @@ -1,192 +0,0 @@ -package deprecated - -import ( - "encoding/json" - "os" - "path/filepath" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/grafana/grafana-plugin-sdk-go/data" - "github.com/stretchr/testify/require" - "github.com/xorcare/pointer" -) - -func TestInsightsMetricsResultToFrame(t *testing.T) { - tests := []struct { - name string - testFile string - metric string - alias string - agg string - dimensions []string - expectedFrame func() *data.Frame - }{ - { - name: "single series", - testFile: "applicationinsights/4-application-insights-response-metrics-no-segment.json", - metric: "value", - agg: "avg", - expectedFrame: func() *data.Frame { - frame := data.NewFrame("", - data.NewField("StartTime", nil, []time.Time{ - time.Date(2019, 9, 13, 1, 2, 3, 456789000, time.UTC), - time.Date(2019, 9, 13, 2, 2, 3, 456789000, time.UTC), - }), - data.NewField("value", nil, []*float64{ - pointer.Float64(1), - pointer.Float64(2), - }), - ) - return frame - }, - }, - { - name: "empty response", - testFile: "applicationinsights/5-application-insights-empty-response.json", - metric: "value", - agg: "avg", - expectedFrame: func() *data.Frame { - frame := data.NewFrame("", data.NewField("StartTime", nil, []time.Time{})) - return frame - }, - }, - { - name: "segmented series", - testFile: "applicationinsights/4-application-insights-response-metrics-segmented.json", - metric: "value", - agg: "avg", - dimensions: []string{"blob"}, - expectedFrame: func() *data.Frame { - frame := data.NewFrame("", - data.NewField("StartTime", nil, []time.Time{ - time.Date(2019, 9, 13, 1, 2, 3, 456789000, time.UTC), - time.Date(2019, 9, 13, 2, 2, 3, 456789000, time.UTC), - }), - data.NewField("value", data.Labels{"blob": "a"}, []*float64{ - pointer.Float64(1), - pointer.Float64(2), - }), - data.NewField("value", data.Labels{"blob": "b"}, []*float64{ - pointer.Float64(3), - pointer.Float64(4), - }), - ) - return frame - }, - }, - { - name: "multi segmented series", - testFile: "applicationinsights/4-application-insights-response-metrics-multi-segmented.json", - metric: "traces/count", - agg: "sum", - dimensions: []string{"client/countryOrRegion", "client/city"}, - expectedFrame: func() *data.Frame { - frame := data.NewFrame("", - data.NewField("StartTime", nil, []time.Time{ - time.Date(2020, 6, 25, 16, 15, 32, 14e7, time.UTC), - time.Date(2020, 6, 25, 16, 16, 0, 0, time.UTC), - }), - data.NewField("traces/count", data.Labels{"client/city": "Tokyo", "client/countryOrRegion": "Japan"}, []*float64{ - nil, - pointer.Float64(1), - }), - data.NewField("traces/count", data.Labels{"client/city": "", "client/countryOrRegion": "United States"}, []*float64{ - nil, - pointer.Float64(11), - }), - data.NewField("traces/count", data.Labels{"client/city": "Chicago", "client/countryOrRegion": "United States"}, []*float64{ - nil, - pointer.Float64(3), - }), - data.NewField("traces/count", data.Labels{"client/city": "Des Moines", "client/countryOrRegion": "United States"}, []*float64{ - pointer.Float64(2), - pointer.Float64(1), - }), - data.NewField("traces/count", data.Labels{"client/city": "Washington", "client/countryOrRegion": "United States"}, []*float64{ - pointer.Float64(2), - nil, - }), - ) - - return frame - }, - }, - { - name: "segmented series with alias", - testFile: "applicationinsights/4-application-insights-response-metrics-multi-segmented.json", - metric: "traces/count", - alias: "{{ metric }}: Country,City: {{ client/countryOrRegion }},{{ client/city }}", - agg: "sum", - dimensions: []string{"client/countryOrRegion", "client/city"}, - expectedFrame: func() *data.Frame { - frame := data.NewFrame("", - data.NewField("StartTime", nil, []time.Time{ - time.Date(2020, 6, 25, 16, 15, 32, 14e7, time.UTC), - time.Date(2020, 6, 25, 16, 16, 0, 0, time.UTC), - }), - data.NewField("traces/count", data.Labels{"client/city": "Tokyo", "client/countryOrRegion": "Japan"}, []*float64{ - nil, - pointer.Float64(1), - }).SetConfig(&data.FieldConfig{DisplayName: "traces/count: Country,City: Japan,Tokyo"}), - data.NewField("traces/count", data.Labels{"client/city": "", "client/countryOrRegion": "United States"}, []*float64{ - nil, - pointer.Float64(11), - }).SetConfig(&data.FieldConfig{DisplayName: "traces/count: Country,City: United States,"}), - data.NewField("traces/count", data.Labels{"client/city": "Chicago", "client/countryOrRegion": "United States"}, []*float64{ - nil, - pointer.Float64(3), - }).SetConfig(&data.FieldConfig{DisplayName: "traces/count: Country,City: United States,Chicago"}), - - data.NewField("traces/count", data.Labels{"client/city": "Des Moines", "client/countryOrRegion": "United States"}, []*float64{ - pointer.Float64(2), - pointer.Float64(1), - }).SetConfig(&data.FieldConfig{DisplayName: "traces/count: Country,City: United States,Des Moines"}), - - data.NewField("traces/count", data.Labels{"client/city": "Washington", "client/countryOrRegion": "United States"}, []*float64{ - pointer.Float64(2), - nil, - }).SetConfig(&data.FieldConfig{DisplayName: "traces/count: Country,City: United States,Washington"}), - ) - - return frame - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - res := loadInsightsMetricsResponse(t, tt.testFile) - - frame, err := InsightsMetricsResultToFrame(res, tt.metric, tt.agg, tt.dimensions) - require.NoError(t, err) - - applyInsightsMetricAlias(frame, tt.alias) - - if diff := cmp.Diff(tt.expectedFrame(), frame, data.FrameTestCompareOptions()...); diff != "" { - t.Errorf("Result mismatch (-want +got):\n%s", diff) - } - }) - } -} - -func loadInsightsMetricsResponse(t *testing.T, name string) MetricsResult { - t.Helper() - - path := filepath.Join("../testdata", name) - // Ignore gosec warning G304 since it's a test - // nolint:gosec - f, err := os.Open(path) - require.NoError(t, err) - defer func() { - err := f.Close() - require.NoError(t, err) - }() - - d := json.NewDecoder(f) - var mr MetricsResult - err = d.Decode(&mr) - require.NoError(t, err) - - return mr -} diff --git a/pkg/tsdb/azuremonitor/deprecated/httpclient.go b/pkg/tsdb/azuremonitor/deprecated/httpclient.go deleted file mode 100644 index 39eecf9458e..00000000000 --- a/pkg/tsdb/azuremonitor/deprecated/httpclient.go +++ /dev/null @@ -1,20 +0,0 @@ -package deprecated - -import ( - "net/http" - - "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" -) - -func GetAppInsightsMiddleware(url, appInsightsApiKey string) httpclient.Middleware { - if appInsightsApiKey != "" && url == AzAppInsights.URL || url == AzChinaAppInsights.URL { - // Inject API-Key for AppInsights - return httpclient.MiddlewareFunc(func(opts httpclient.Options, next http.RoundTripper) http.RoundTripper { - return httpclient.RoundTripperFunc(func(req *http.Request) (*http.Response, error) { - req.Header.Set("X-API-Key", appInsightsApiKey) - return next.RoundTrip(req) - }) - }) - } - return nil -} diff --git a/pkg/tsdb/azuremonitor/deprecated/insights-analytics-datasource.go b/pkg/tsdb/azuremonitor/deprecated/insights-analytics-datasource.go deleted file mode 100644 index 00d29951fca..00000000000 --- a/pkg/tsdb/azuremonitor/deprecated/insights-analytics-datasource.go +++ /dev/null @@ -1,190 +0,0 @@ -package deprecated - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "path" - - "github.com/grafana/grafana-plugin-sdk-go/backend" - "github.com/grafana/grafana-plugin-sdk-go/data" - "go.opentelemetry.io/otel/attribute" - - "github.com/grafana/grafana/pkg/infra/tracing" - "github.com/grafana/grafana/pkg/tsdb/azuremonitor/azlog" - "github.com/grafana/grafana/pkg/tsdb/azuremonitor/loganalytics" - "github.com/grafana/grafana/pkg/tsdb/azuremonitor/macros" - "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" - "github.com/grafana/grafana/pkg/util/errutil" -) - -type InsightsAnalyticsDatasource struct { - Proxy types.ServiceProxy -} - -type InsightsAnalyticsQuery struct { - RefID string - - RawQuery string - InterpolatedQuery string - - ResultFormat string - - Params url.Values - Target string -} - -func (e *InsightsAnalyticsDatasource) ResourceRequest(rw http.ResponseWriter, req *http.Request, cli *http.Client) { - e.Proxy.Do(rw, req, cli) -} - -func (e *InsightsAnalyticsDatasource) ExecuteTimeSeriesQuery(ctx context.Context, - originalQueries []backend.DataQuery, dsInfo types.DatasourceInfo, client *http.Client, - url string, tracer tracing.Tracer) (*backend.QueryDataResponse, error) { - result := backend.NewQueryDataResponse() - - queries, err := e.buildQueries(originalQueries, dsInfo) - if err != nil { - return nil, err - } - - for _, query := range queries { - result.Responses[query.RefID] = e.executeQuery(ctx, query, dsInfo, client, url, tracer) - } - - return result, nil -} - -func (e *InsightsAnalyticsDatasource) buildQueries(queries []backend.DataQuery, dsInfo types.DatasourceInfo) ([]*InsightsAnalyticsQuery, error) { - iaQueries := []*InsightsAnalyticsQuery{} - - for _, query := range queries { - qm := InsightsAnalyticsQuery{} - queryJSONModel := insightsAnalyticsJSONQuery{} - err := json.Unmarshal(query.JSON, &queryJSONModel) - if err != nil { - return nil, fmt.Errorf("failed to decode the Azure Application Insights Analytics query object from JSON: %w", err) - } - - qm.RawQuery = queryJSONModel.InsightsAnalytics.Query - qm.ResultFormat = queryJSONModel.InsightsAnalytics.ResultFormat - qm.RefID = query.RefID - - if qm.RawQuery == "" { - return nil, fmt.Errorf("query is missing query string property") - } - - qm.InterpolatedQuery, err = macros.KqlInterpolate(query, dsInfo, qm.RawQuery) - if err != nil { - return nil, err - } - qm.Params = url.Values{} - qm.Params.Add("query", qm.InterpolatedQuery) - - qm.Target = qm.Params.Encode() - iaQueries = append(iaQueries, &qm) - } - - return iaQueries, nil -} - -func (e *InsightsAnalyticsDatasource) executeQuery(ctx context.Context, query *InsightsAnalyticsQuery, dsInfo types.DatasourceInfo, client *http.Client, - url string, tracer tracing.Tracer) backend.DataResponse { - dataResponse := backend.DataResponse{} - - dataResponseError := func(err error) backend.DataResponse { - dataResponse.Error = err - return dataResponse - } - - req, err := e.createRequest(ctx, dsInfo, url) - if err != nil { - return dataResponseError(err) - } - req.URL.Path = path.Join(req.URL.Path, "query") - req.URL.RawQuery = query.Params.Encode() - - ctx, span := tracer.Start(ctx, "application insights analytics query") - span.SetAttributes("target", query.Target, attribute.Key("target").String(query.Target)) - span.SetAttributes("datasource_id", dsInfo.DatasourceID, attribute.Key("datasource_id").Int64(dsInfo.DatasourceID)) - span.SetAttributes("org_id", dsInfo.OrgID, attribute.Key("org_id").Int64(dsInfo.OrgID)) - - defer span.End() - tracer.Inject(ctx, req.Header, span) - - if err != nil { - azlog.Warn("failed to inject global tracer") - } - - azlog.Debug("ApplicationInsights", "Request URL", req.URL.String()) - res, err := client.Do(req) - if err != nil { - return dataResponseError(err) - } - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - return dataResponseError(err) - } - defer func() { - if err := res.Body.Close(); err != nil { - azlog.Warn("Failed to close response body", "err", err) - } - }() - - if res.StatusCode/100 != 2 { - azlog.Debug("Request failed", "status", res.Status, "body", string(body)) - return dataResponseError(fmt.Errorf("request failed, status: %s, body: %s", res.Status, body)) - } - var logResponse loganalytics.AzureLogAnalyticsResponse - d := json.NewDecoder(bytes.NewReader(body)) - d.UseNumber() - err = d.Decode(&logResponse) - if err != nil { - return dataResponseError(err) - } - - t, err := logResponse.GetPrimaryResultTable() - if err != nil { - return dataResponseError(err) - } - - frame, err := loganalytics.ResponseTableToFrame(t) - if err != nil { - return dataResponseError(err) - } - - if query.ResultFormat == types.TimeSeries { - tsSchema := frame.TimeSeriesSchema() - if tsSchema.Type == data.TimeSeriesTypeLong { - wideFrame, err := data.LongToWide(frame, nil) - if err == nil { - frame = wideFrame - } else { - frame.AppendNotices(data.Notice{ - Severity: data.NoticeSeverityWarning, - Text: "could not convert frame to time series, returning raw table: " + err.Error(), - }) - } - } - } - dataResponse.Frames = data.Frames{frame} - - return dataResponse -} - -func (e *InsightsAnalyticsDatasource) createRequest(ctx context.Context, dsInfo types.DatasourceInfo, url string) (*http.Request, error) { - appInsightsAppID := dsInfo.Settings.AppInsightsAppId - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - azlog.Debug("Failed to create request", "error", err) - return nil, errutil.Wrap("Failed to create request", err) - } - req.URL.Path = fmt.Sprintf("/v1/apps/%s", appInsightsAppID) - return req, nil -} diff --git a/pkg/tsdb/azuremonitor/deprecated/insights-analytics-datasource_test.go b/pkg/tsdb/azuremonitor/deprecated/insights-analytics-datasource_test.go deleted file mode 100644 index 959c8cfe9c1..00000000000 --- a/pkg/tsdb/azuremonitor/deprecated/insights-analytics-datasource_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package deprecated - -import ( - "context" - "net/http" - "testing" - - "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" - "github.com/stretchr/testify/require" -) - -func TestInsightsAnalyticsCreateRequest(t *testing.T) { - ctx := context.Background() - url := "http://ds" - dsInfo := types.DatasourceInfo{ - Settings: types.AzureMonitorSettings{AppInsightsAppId: "foo"}, - DecryptedSecureJSONData: map[string]string{ - "appInsightsApiKey": "key", - }, - } - - tests := []struct { - name string - expectedURL string - expectedHeaders http.Header - Err require.ErrorAssertionFunc - }{ - { - name: "creates a request", - expectedURL: "http://ds/v1/apps/foo", - Err: require.NoError, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ds := InsightsAnalyticsDatasource{} - req, err := ds.createRequest(ctx, dsInfo, url) - tt.Err(t, err) - if req.URL.String() != tt.expectedURL { - t.Errorf("Expecting %s, got %s", tt.expectedURL, req.URL.String()) - } - }) - } -} diff --git a/pkg/tsdb/azuremonitor/deprecated/routes.go b/pkg/tsdb/azuremonitor/deprecated/routes.go deleted file mode 100644 index 9b2d5e58823..00000000000 --- a/pkg/tsdb/azuremonitor/deprecated/routes.go +++ /dev/null @@ -1,23 +0,0 @@ -package deprecated - -import ( - "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" -) - -// Azure cloud query types -const ( - AppInsights = "Application Insights" - InsightsAnalytics = "Insights Analytics" -) - -var AzAppInsights = types.AzRoute{ - URL: "https://api.applicationinsights.io", - Scopes: []string{}, - Headers: map[string]string{"x-ms-app": "Grafana"}, -} - -var AzChinaAppInsights = types.AzRoute{ - URL: "https://api.applicationinsights.azure.cn", - Scopes: []string{}, - Headers: map[string]string{"x-ms-app": "Grafana"}, -} diff --git a/pkg/tsdb/azuremonitor/deprecated/types.go b/pkg/tsdb/azuremonitor/deprecated/types.go deleted file mode 100644 index 487f28bfdfc..00000000000 --- a/pkg/tsdb/azuremonitor/deprecated/types.go +++ /dev/null @@ -1,72 +0,0 @@ -package deprecated - -import ( - "encoding/json" - "fmt" - "strings" -) - -// insightsJSONQuery is the frontend JSON query model for an Azure Application Insights query. -type insightsJSONQuery struct { - AppInsights struct { - Aggregation string `json:"aggregation"` - Alias string `json:"alias"` - AllowedTimeGrainsMs []int64 `json:"allowedTimeGrainsMs"` - Dimensions InsightsDimensions `json:"dimension"` - DimensionFilter string `json:"dimensionFilter"` - MetricName string `json:"metricName"` - TimeGrain string `json:"timeGrain"` - } `json:"appInsights"` - Raw *bool `json:"raw"` -} - -// InsightsDimensions will unmarshal from a JSON string, or an array of strings, -// into a string array. This exists to support an older query format which is updated -// when a user saves the query or it is sent from the front end, but may not be when -// alerting fetches the model. -type InsightsDimensions []string - -// UnmarshalJSON fulfills the json.Unmarshaler interface type. -func (s *InsightsDimensions) UnmarshalJSON(data []byte) error { - *s = InsightsDimensions{} - if string(data) == "null" || string(data) == "" { - return nil - } - if strings.ToLower(string(data)) == `"none"` { - return nil - } - if data[0] == '[' { - var sa []string - err := json.Unmarshal(data, &sa) - if err != nil { - return err - } - dimensions := []string{} - for _, v := range sa { - if v == "none" || v == "None" { - continue - } - dimensions = append(dimensions, v) - } - *s = InsightsDimensions(dimensions) - return nil - } - - var str string - err := json.Unmarshal(data, &str) - if err != nil { - return fmt.Errorf("could not parse %q as string or array: %w", string(data), err) - } - if str != "" { - *s = InsightsDimensions{str} - return nil - } - return nil -} - -type insightsAnalyticsJSONQuery struct { - InsightsAnalytics struct { - Query string `json:"query"` - ResultFormat string `json:"resultFormat"` - } `json:"insightsAnalytics"` -} diff --git a/pkg/tsdb/azuremonitor/httpclient.go b/pkg/tsdb/azuremonitor/httpclient.go index a8dc2ad377d..12a483810ab 100644 --- a/pkg/tsdb/azuremonitor/httpclient.go +++ b/pkg/tsdb/azuremonitor/httpclient.go @@ -8,30 +8,12 @@ import ( "github.com/grafana/grafana/pkg/infra/httpclient" "github.com/grafana/grafana/pkg/setting" - "github.com/grafana/grafana/pkg/tsdb/azuremonitor/deprecated" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" ) -func getMiddlewares(route types.AzRoute, model types.DatasourceInfo) ([]sdkhttpclient.Middleware, error) { - var middlewares []sdkhttpclient.Middleware - - // Remove with Grafana 9 - if apiKeyMiddleware := deprecated.GetAppInsightsMiddleware(route.URL, model.DecryptedSecureJSONData["appInsightsApiKey"]); apiKeyMiddleware != nil { - middlewares = append(middlewares, apiKeyMiddleware) - } - - return middlewares, nil -} - func newHTTPClient(route types.AzRoute, model types.DatasourceInfo, cfg *setting.Cfg, clientProvider httpclient.Provider) (*http.Client, error) { - m, err := getMiddlewares(route, model) - if err != nil { - return nil, err - } - opts := sdkhttpclient.Options{ - Headers: route.Headers, - Middlewares: m, + Headers: route.Headers, } // Use Azure credentials if the route has OAuth scopes configured diff --git a/pkg/tsdb/azuremonitor/httpclient_test.go b/pkg/tsdb/azuremonitor/httpclient_test.go index 0867acd0144..5e127c38f70 100644 --- a/pkg/tsdb/azuremonitor/httpclient_test.go +++ b/pkg/tsdb/azuremonitor/httpclient_test.go @@ -10,62 +10,11 @@ import ( "github.com/grafana/grafana/pkg/infra/httpclient" "github.com/grafana/grafana/pkg/setting" - "github.com/grafana/grafana/pkg/tsdb/azuremonitor/deprecated" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestHttpClient_Middlewares(t *testing.T) { - tests := []struct { - name string - route types.AzRoute - model types.DatasourceInfo - expectedMiddlewares int - Err require.ErrorAssertionFunc - }{ - { - name: "creates an HTTP client with a middleware due to an app key", - route: types.AzRoute{ - URL: deprecated.AzAppInsights.URL, - Scopes: []string{}, - }, - model: types.DatasourceInfo{ - Credentials: &azcredentials.AzureClientSecretCredentials{}, - DecryptedSecureJSONData: map[string]string{ - "appInsightsApiKey": "foo", - }, - }, - expectedMiddlewares: 1, - Err: require.NoError, - }, - { - name: "creates an HTTP client without a middleware", - route: types.AzRoute{ - URL: "http://route", - Scopes: []string{}, - }, - model: types.DatasourceInfo{ - Credentials: &azcredentials.AzureClientSecretCredentials{}, - }, - expectedMiddlewares: 0, - Err: require.NoError, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - m, err := getMiddlewares(tt.route, tt.model) - require.NoError(t, err) - - // Cannot test that the cli middleware works properly since the azcore sdk - // rejects the TLS certs (if provided) - if len(m) != tt.expectedMiddlewares { - t.Errorf("Unexpected middlewares: %v", m) - } - }) - } -} - func TestHttpClient_AzureCredentials(t *testing.T) { model := types.DatasourceInfo{ Credentials: &azcredentials.AzureManagedIdentityCredentials{}, @@ -76,7 +25,6 @@ func TestHttpClient_AzureCredentials(t *testing.T) { t.Run("should have Azure middleware when scopes provided", func(t *testing.T) { route := types.AzRoute{ - URL: deprecated.AzAppInsights.URL, Scopes: []string{"https://management.azure.com/.default"}, } @@ -90,7 +38,6 @@ func TestHttpClient_AzureCredentials(t *testing.T) { t.Run("should not have Azure middleware when scopes are not provided", func(t *testing.T) { route := types.AzRoute{ - URL: deprecated.AzAppInsights.URL, Scopes: []string{}, } diff --git a/pkg/tsdb/azuremonitor/routes.go b/pkg/tsdb/azuremonitor/routes.go index 1a883826f5f..c0a7609845b 100644 --- a/pkg/tsdb/azuremonitor/routes.go +++ b/pkg/tsdb/azuremonitor/routes.go @@ -3,7 +3,6 @@ package azuremonitor import ( "github.com/grafana/grafana-azure-sdk-go/azsettings" - "github.com/grafana/grafana/pkg/tsdb/azuremonitor/deprecated" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" ) @@ -61,11 +60,9 @@ var ( // and the service to query (e.g. Azure Monitor or Azure Log Analytics) routes = map[string]map[string]types.AzRoute{ azsettings.AzurePublic: { - azureMonitor: azManagement, - azureLogAnalytics: azLogAnalytics, - azureResourceGraph: azManagement, - deprecated.AppInsights: deprecated.AzAppInsights, - deprecated.InsightsAnalytics: deprecated.AzAppInsights, + azureMonitor: azManagement, + azureLogAnalytics: azLogAnalytics, + azureResourceGraph: azManagement, }, azsettings.AzureUSGovernment: { azureMonitor: azUSGovManagement, @@ -76,11 +73,9 @@ var ( azureMonitor: azGermanyManagement, }, azsettings.AzureChina: { - azureMonitor: azChinaManagement, - azureLogAnalytics: azChinaLogAnalytics, - azureResourceGraph: azChinaManagement, - deprecated.AppInsights: deprecated.AzChinaAppInsights, - deprecated.InsightsAnalytics: deprecated.AzChinaAppInsights, + azureMonitor: azChinaManagement, + azureLogAnalytics: azChinaLogAnalytics, + azureResourceGraph: azChinaManagement, }, } ) diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/query.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/query.ts index 8ed51200cea..cac782301ec 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/query.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/query.ts @@ -2,8 +2,6 @@ import { AzureMonitorQuery, AzureQueryType } from '../types'; export default function createMockQuery(): AzureMonitorQuery { return { - appInsights: undefined, // The actualy shape of this at runtime disagrees with the ts interface - azureLogAnalytics: { query: '//change this example to create your own time series query\n //the table to query (e.g. Usage, Heartbeat, Perf)\n| where $__timeFilter(TimeGenerated) //this is a macro used to show the full chart’s time range, choose the datetime column here\n| summarize count() by , bin(TimeGenerated, $__interval) //change “group by column” to a column in your table, such as “Computer”. The $__interval macro is used to auto-select the time grain. Can also use 1h, 5m etc.\n| order by TimeGenerated asc', @@ -34,11 +32,6 @@ export default function createMockQuery(): AzureMonitorQuery { top: '10', }, - insightsAnalytics: { - query: '', - resultFormat: 'time_series', - }, - queryType: AzureQueryType.AzureMonitor, refId: 'A', subscription: '99999999-cccc-bbbb-aaaa-9106972f9572', diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ConfigEditor.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ConfigEditor.test.tsx index 39c29eb5098..259d155f322 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ConfigEditor.test.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ConfigEditor.test.tsx @@ -45,20 +45,4 @@ describe('AppInsights ConfigEditor', () => { expect(screen.queryByText('Azure Application Insights')).not.toBeInTheDocument(); }); - - it('should render application insights config for data sources using application insights', () => { - const options = { - ...baseOptions, - jsonData: { - ...jsonData, - appInsightsAppId: 'abc-123', - }, - secureJsonFields: { - appInsightsApiKey: true, - }, - }; - render(); - - expect(screen.queryByText('Azure Application Insights')).toBeInTheDocument(); - }); }); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ConfigEditor.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ConfigEditor.tsx index 685c313c390..a4ea6ce39d9 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ConfigEditor.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/ConfigEditor.tsx @@ -9,9 +9,6 @@ import { AzureDataSourceJsonData, AzureDataSourceSecureJsonData, AzureDataSource import { routeNames } from '../utils/common'; import { MonitorConfig } from './MonitorConfig'; -import { AnalyticsConfig } from './deprecated/components/AnalyticsConfig'; -import { InsightsConfig } from './deprecated/components/InsightsConfig'; -import { gtGrafana9, isAppInsightsConfigured } from './deprecated/utils'; export type Props = DataSourcePluginOptionsEditorProps; @@ -91,14 +88,6 @@ export class ConfigEditor extends PureComponent { return ( <> - {/* Remove with Grafana 9 */} - {!gtGrafana9() && ( - <> - - {isAppInsightsConfigured(options) && } - - )} - {/* ===================== */} {error && (

{error.description}

diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/QueryEditor/QueryEditor.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/QueryEditor/QueryEditor.test.tsx index 1ef54499861..d23ec33fcd3 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/QueryEditor/QueryEditor.test.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/QueryEditor/QueryEditor.test.tsx @@ -1,6 +1,5 @@ import { render, screen, waitFor } from '@testing-library/react'; import React from 'react'; -import selectEvent from 'react-select-event'; import { config } from '@grafana/runtime'; import * as ui from '@grafana/ui'; @@ -8,7 +7,7 @@ import * as ui from '@grafana/ui'; import createMockDatasource from '../../__mocks__/datasource'; import { invalidNamespaceError } from '../../__mocks__/errors'; import createMockQuery from '../../__mocks__/query'; -import { AzureQueryType, DeprecatedAzureQueryType } from '../../types'; +import { AzureQueryType } from '../../types'; import QueryEditor from './QueryEditor'; @@ -43,34 +42,6 @@ describe('Azure Monitor QueryEditor', () => { await waitFor(() => expect(screen.queryByTestId('azure-monitor-logs-query-editor')).toBeInTheDocument()); }); - it('renders the ApplicationInsights query editor when the query type is Application Insights and renders values in disabled inputs', async () => { - const mockDatasource = createMockDatasource(); - const mockQuery = { - ...createMockQuery(), - queryType: DeprecatedAzureQueryType.ApplicationInsights, - appInsights: { - metricName: 'requests/count', - timeGrain: 'PT1H', - timeGrainCount: '1', - timeGrainType: 'specific', - timeGrainUnit: 'hour', - aggregation: 'average', - dimension: ['request/name'], - dimensionFilter: "request/name eq 'GET Home/Index'", - alias: '{{ request/name }}', - }, - }; - - render( {}} onRunQuery={() => {}} />); - await waitFor(() => - expect(screen.queryByTestId('azure-monitor-application-insights-query-editor')).toBeInTheDocument() - ); - - const metricInput = await screen.getByLabelText('Metric'); - expect(metricInput).toBeDisabled(); - expect(metricInput).toHaveValue('requests/count'); - }); - it('changes the query type when selected', async () => { const mockDatasource = createMockDatasource(); const mockQuery = createMockQuery(); @@ -98,42 +69,6 @@ describe('Azure Monitor QueryEditor', () => { expect(screen.getByText("The resource namespace 'grafanadev' is invalid.")).toBeInTheDocument(); }); - it('hides deprecated services', async () => { - const mockDatasource = createMockDatasource(); - const mockQuery = { - ...createMockQuery(), - queryType: AzureQueryType.AzureMonitor, - }; - - render( {}} onRunQuery={() => {}} />); - await waitFor(() => expect(screen.getByTestId('azure-monitor-metrics-query-editor')).toBeInTheDocument()); - - const metrics = await screen.findByLabelText('Service'); - selectEvent.openMenu(metrics); - - expect(screen.queryByText('Application Insights')).not.toBeInTheDocument(); - }); - - it("shows deprecated services when they're selected", async () => { - const mockDatasource = createMockDatasource(); - const mockQuery = { - ...createMockQuery(), - queryType: DeprecatedAzureQueryType.ApplicationInsights, - }; - - render( {}} onRunQuery={() => {}} />); - await waitFor(() => - expect(screen.getByTestId('azure-monitor-application-insights-query-editor')).toBeInTheDocument() - ); - - expect(screen.queryByText('Application Insights')).toBeInTheDocument(); - - const metrics = await screen.findByLabelText('Service'); - await ui.selectOptionInTest(metrics, 'Logs'); - - expect(screen.queryByText('Application Insights')).toBeInTheDocument(); - }); - it('renders the new query editor for metrics when enabled with a feature toggle', async () => { const originalConfigValue = config.featureToggles.azureMonitorResourcePickerForMetrics; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/QueryEditor/QueryEditor.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/QueryEditor/QueryEditor.tsx index f0d168ef5c6..6b7322b7a35 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/QueryEditor/QueryEditor.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/QueryEditor/QueryEditor.tsx @@ -3,7 +3,7 @@ import React, { useCallback, useMemo } from 'react'; import { QueryEditorProps } from '@grafana/data'; import { config } from '@grafana/runtime'; -import { Alert } from '@grafana/ui'; +import { Alert, CodeEditor } from '@grafana/ui'; import AzureMonitorDatasource from '../../datasource'; import { @@ -12,7 +12,6 @@ import { AzureMonitorOption, AzureMonitorQuery, AzureQueryType, - DeprecatedAzureQueryType, } from '../../types'; import useLastError from '../../utils/useLastError'; import ArgQueryEditor from '../ArgQueryEditor'; @@ -20,9 +19,6 @@ import LogsQueryEditor from '../LogsQueryEditor'; import MetricsQueryEditor from '../MetricsQueryEditor'; import NewMetricsQueryEditor from '../NewMetricsQueryEditor/MetricsQueryEditor'; import { Space } from '../Space'; -import ApplicationInsightsEditor from '../deprecated/components/ApplicationInsightsEditor'; -import InsightsAnalyticsEditor from '../deprecated/components/InsightsAnalyticsEditor'; -import { gtGrafana9 } from '../deprecated/utils'; import QueryTypeField from './QueryTypeField'; import usePreparedQuery from './usePreparedQuery'; @@ -145,46 +141,26 @@ const EditorForQueryType: React.FC = ({ /> ); - /** Remove with Grafana 9 */ - case DeprecatedAzureQueryType.ApplicationInsights: - if (gtGrafana9()) { - return ( - - Application Insights has been deprecated.{' '} - - Use the Metrics service instead - - . - - ); - } - return ; - - case DeprecatedAzureQueryType.InsightsAnalytics: - if (gtGrafana9()) { - return ( - - Insight Analytics has been deprecated.{' '} - - Queries can be written with Kusto in the Logs query type by selecting your Application Insights resource - - . - - ); - } - return ; - /** ===================== */ - default: - return ; + const type = query.queryType as unknown; + return ( + + {(type === 'Application Insights' || type === 'Insights Analytics') && ( + <> + {type} was deprecated in Grafana 9. See the{' '} + + deprecation notice + {' '} + to get more information about how to migrate your queries. This is the current query definition: + + + )} + + ); } return null; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/QueryEditor/QueryTypeField.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/QueryEditor/QueryTypeField.tsx index 0ea008631cd..56bf98edc6e 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/QueryEditor/QueryTypeField.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/QueryEditor/QueryTypeField.tsx @@ -1,11 +1,10 @@ -import React, { useCallback, useState } from 'react'; +import React, { useCallback } from 'react'; import { SelectableValue } from '@grafana/data'; import { Select } from '@grafana/ui'; -import { AzureMonitorQuery, AzureQueryType, DeprecatedAzureQueryType } from '../../types'; +import { AzureMonitorQuery, AzureQueryType } from '../../types'; import { Field } from '../Field'; -import { gtGrafana9 } from '../deprecated/utils'; interface QueryTypeFieldProps { query: AzureMonitorQuery; @@ -13,29 +12,14 @@ interface QueryTypeFieldProps { } const QueryTypeField: React.FC = ({ query, onQueryChange }) => { - // Use useState to capture the initial value on first mount. We're not interested in when it changes - // We only show App Insights and Insights Analytics if they were initially selected. Otherwise, hide them. - const [initialQueryType] = useState(query.queryType); - - const queryTypes: Array<{ value: AzureQueryType | DeprecatedAzureQueryType; label: string }> = [ + const queryTypes: Array<{ value: AzureQueryType; label: string }> = [ { value: AzureQueryType.AzureMonitor, label: 'Metrics' }, { value: AzureQueryType.LogAnalytics, label: 'Logs' }, { value: AzureQueryType.AzureResourceGraph, label: 'Azure Resource Graph' }, ]; - if ( - !gtGrafana9() && - (initialQueryType === DeprecatedAzureQueryType.ApplicationInsights || - initialQueryType === DeprecatedAzureQueryType.InsightsAnalytics) - ) { - queryTypes.push( - { value: DeprecatedAzureQueryType.ApplicationInsights, label: 'Application Insights' }, - { value: DeprecatedAzureQueryType.InsightsAnalytics, label: 'Insights Analytics' } - ); - } - const handleChange = useCallback( - (change: SelectableValue) => { + (change: SelectableValue) => { change.value && onQueryChange({ ...query, diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/app_insights/app_insights_datasource.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/app_insights/app_insights_datasource.test.ts deleted file mode 100644 index bd50d3b34c1..00000000000 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/app_insights/app_insights_datasource.test.ts +++ /dev/null @@ -1,463 +0,0 @@ -import { lastValueFrom, of } from 'rxjs'; - -import { DataFrame, getFrameDisplayName, toUtc } from '@grafana/data'; -import { setBackendSrv } from '@grafana/runtime'; -import { backendSrv } from 'app/core/services/backend_srv'; -import { TemplateSrv } from 'app/features/templating/template_srv'; - -import AppInsightsDatasource from './app_insights_datasource'; - -const templateSrv = new TemplateSrv(); - -jest.mock('app/core/services/backend_srv'); -jest.mock('@grafana/runtime', () => ({ - ...(jest.requireActual('@grafana/runtime') as unknown as object), - getBackendSrv: () => backendSrv, - getTemplateSrv: () => templateSrv, -})); - -describe('AppInsightsDatasource', () => { - const fetchMock = jest.spyOn(backendSrv, 'fetch'); - - const ctx: any = {}; - - beforeEach(() => { - jest.clearAllMocks(); - setBackendSrv(backendSrv); - - ctx.instanceSettings = { - jsonData: { appInsightsAppId: '3ad4400f-ea7d-465d-a8fb-43fb20555d85' }, - url: 'http://appinsightsapi', - }; - - ctx.ds = new AppInsightsDatasource(ctx.instanceSettings); - }); - - describe('When performing testDatasource', () => { - describe('and a list of metrics is returned', () => { - const response = { - metrics: { - 'requests/count': { - displayName: 'Server requests', - defaultAggregation: 'sum', - }, - 'requests/duration': { - displayName: 'Server requests', - defaultAggregation: 'sum', - }, - }, - dimensions: { - 'request/source': { - displayName: 'Request source', - }, - }, - }; - - beforeEach(() => { - ctx.ds.getResource = jest.fn().mockImplementation(() => { - return Promise.resolve(response); - }); - }); - - it('should return success status', () => { - return ctx.ds.testDatasource().then((results: any) => { - expect(results.status).toEqual('success'); - }); - }); - }); - - describe('and a PathNotFoundError error is returned', () => { - const error = { - data: { - error: { - code: 'PathNotFoundError', - message: `An error message.`, - }, - }, - status: 404, - statusText: 'Not Found', - }; - - beforeEach(() => { - ctx.ds.getResource = jest.fn().mockImplementation(() => { - return Promise.reject(error); - }); - }); - - it.skip('should return error status and a detailed error message', () => { - return ctx.ds.testDatasource().then((results: any) => { - expect(results.status).toEqual('error'); - expect(results.message).toEqual( - '1. Application Insights: Not Found: Invalid Application Id for Application Insights service. ' - ); - }); - }); - }); - - describe('and an error is returned', () => { - const error = { - data: { - error: { - code: 'SomeOtherError', - message: `An error message.`, - }, - }, - status: 500, - statusText: 'Error', - }; - - beforeEach(() => { - ctx.ds.getResource = jest.fn().mockImplementation(() => { - return Promise.reject(error); - }); - }); - - it.skip('should return error status and a detailed error message', () => { - return ctx.ds.testDatasource().then((results: any) => { - expect(results.status).toEqual('error'); - expect(results.message).toEqual('1. Application Insights: Error: SomeOtherError. An error message. '); - }); - }); - }); - }); - - describe('When performing raw query', () => { - const queryString = - 'metrics ' + - '| where $__timeFilter(timestamp) ' + - '| where name == "testMetrics" ' + - '| summarize max=max(valueMax) by bin(timestamp, $__interval), partition'; - - const options = { - range: { - from: toUtc('2017-08-22T20:00:00Z'), - to: toUtc('2017-08-22T23:59:00Z'), - }, - targets: [ - { - apiVersion: '2016-09-01', - refId: 'A', - queryType: 'Application Insights', - appInsights: { - rawQuery: true, - rawQueryString: queryString, - timeColumn: 'timestamp', - valueColumn: 'max', - segmentColumn: undefined as unknown as string, - }, - }, - ], - }; - - describe('with no grouping', () => { - const response: any = { - results: { - A: { - refId: 'A', - meta: {}, - series: [ - { - name: 'PrimaryResult', - points: [[2.2075, 1558278660000]], - }, - ], - tables: null, - }, - }, - }; - - beforeEach(() => { - fetchMock.mockImplementation((options: any) => { - expect(options.url).toContain('/api/ds/query'); - expect(options.data.queries.length).toBe(1); - expect(options.data.queries[0].refId).toBe('A'); - return of({ data: response, status: 200 } as any); - }); - }); - - it('should return a list of datapoints', () => { - return lastValueFrom(ctx.ds.query(options)).then((results: any) => { - expect(results.data.length).toBe(1); - const data = results.data[0] as DataFrame; - expect(getFrameDisplayName(data)).toEqual('PrimaryResult'); - expect(data.fields[0].values.length).toEqual(1); - expect(data.fields[0].values.get(0)).toEqual(1558278660000); - expect(data.fields[1].values.get(0)).toEqual(2.2075); - }); - }); - }); - - describe('with grouping', () => { - const response: any = { - results: { - A: { - refId: 'A', - meta: {}, - series: [ - { - name: 'paritionA', - points: [[2.2075, 1558278660000]], - }, - ], - tables: null, - }, - }, - }; - - beforeEach(() => { - options.targets[0].appInsights.segmentColumn = 'partition'; - fetchMock.mockImplementation((options: any) => { - expect(options.url).toContain('/api/ds/query'); - expect(options.data.queries.length).toBe(1); - expect(options.data.queries[0].refId).toBe('A'); - return of({ data: response, status: 200 } as any); - }); - }); - - it('should return a list of datapoints', () => { - return lastValueFrom(ctx.ds.query(options)).then((results: any) => { - expect(results.data.length).toBe(1); - const data = results.data[0] as DataFrame; - expect(getFrameDisplayName(data)).toEqual('paritionA'); - expect(data.fields[0].values.length).toEqual(1); - expect(data.fields[0].values.get(0)).toEqual(1558278660000); - expect(data.fields[1].values.get(0)).toEqual(2.2075); - }); - }); - }); - }); - - describe('When performing metric query', () => { - const options = { - range: { - from: toUtc('2017-08-22T20:00:00Z'), - to: toUtc('2017-08-22T23:59:00Z'), - }, - targets: [ - { - apiVersion: '2016-09-01', - refId: 'A', - queryType: 'Application Insights', - appInsights: { - metricName: 'exceptions/server', - dimension: '', - timeGrain: 'none', - }, - }, - ], - }; - - describe('and with a single value', () => { - const response: any = { - results: { - A: { - refId: 'A', - meta: {}, - series: [ - { - name: 'exceptions/server', - points: [[2.2075, 1558278660000]], - }, - ], - tables: null, - }, - }, - }; - - beforeEach(() => { - fetchMock.mockImplementation((options: any) => { - expect(options.url).toContain('/api/ds/query'); - expect(options.data.queries.length).toBe(1); - expect(options.data.queries[0].refId).toBe('A'); - expect(options.data.queries[0].appInsights.rawQueryString).toBeUndefined(); - expect(options.data.queries[0].appInsights.metricName).toBe('exceptions/server'); - return of({ data: response, status: 200 } as any); - }); - }); - - it('should return a single datapoint', () => { - return lastValueFrom(ctx.ds.query(options)).then((results: any) => { - expect(results.data.length).toBe(1); - const data = results.data[0] as DataFrame; - expect(getFrameDisplayName(data)).toEqual('exceptions/server'); - expect(data.fields[0].values.get(0)).toEqual(1558278660000); - expect(data.fields[1].values.get(0)).toEqual(2.2075); - }); - }); - }); - - describe('and with an interval group and without a segment group by', () => { - const response: any = { - results: { - A: { - refId: 'A', - meta: {}, - series: [ - { - name: 'exceptions/server', - points: [ - [3, 1504108800000], - [6, 1504112400000], - ], - }, - ], - tables: null, - }, - }, - }; - - beforeEach(() => { - options.targets[0].appInsights.timeGrain = 'PT30M'; - fetchMock.mockImplementation((options: any) => { - expect(options.url).toContain('/api/ds/query'); - expect(options.data.queries[0].refId).toBe('A'); - expect(options.data.queries[0].appInsights.query).toBeUndefined(); - expect(options.data.queries[0].appInsights.metricName).toBe('exceptions/server'); - expect(options.data.queries[0].appInsights.timeGrain).toBe('PT30M'); - return of({ data: response, status: 200 } as any); - }); - }); - - it('should return a list of datapoints', () => { - return lastValueFrom(ctx.ds.query(options)).then((results: any) => { - expect(results.data.length).toBe(1); - const data = results.data[0] as DataFrame; - expect(getFrameDisplayName(data)).toEqual('exceptions/server'); - expect(data.fields[0].values.length).toEqual(2); - expect(data.fields[0].values.get(0)).toEqual(1504108800000); - expect(data.fields[1].values.get(0)).toEqual(3); - expect(data.fields[0].values.get(1)).toEqual(1504112400000); - expect(data.fields[1].values.get(1)).toEqual(6); - }); - }); - }); - - describe('and with a group by', () => { - const response: any = { - results: { - A: { - refId: 'A', - meta: {}, - series: [ - { - name: 'exceptions/server{client/city="Miami"}', - points: [ - [10, 1504108800000], - [20, 1504112400000], - ], - }, - { - name: 'exceptions/server{client/city="San Antonio"}', - points: [ - [1, 1504108800000], - [2, 1504112400000], - ], - }, - ], - tables: null, - }, - }, - }; - - describe('and with no alias specified', () => { - beforeEach(() => { - options.targets[0].appInsights.dimension = 'client/city'; - - fetchMock.mockImplementation((options: any) => { - expect(options.url).toContain('/api/ds/query'); - expect(options.data.queries[0].appInsights.rawQueryString).toBeUndefined(); - expect(options.data.queries[0].appInsights.metricName).toBe('exceptions/server'); - expect([...options.data.queries[0].appInsights.dimension]).toMatchObject(['client/city']); - return of({ data: response, status: 200 } as any); - }); - }); - - it('should return a list of datapoints', () => { - return lastValueFrom(ctx.ds.query(options)).then((results: any) => { - expect(results.data.length).toBe(2); - let data = results.data[0] as DataFrame; - expect(getFrameDisplayName(data)).toEqual('exceptions/server{client/city="Miami"}'); - expect(data.fields[1].values.length).toEqual(2); - expect(data.fields[0].values.get(0)).toEqual(1504108800000); - expect(data.fields[1].values.get(0)).toEqual(10); - expect(data.fields[0].values.get(1)).toEqual(1504112400000); - expect(data.fields[1].values.get(1)).toEqual(20); - data = results.data[1] as DataFrame; - expect(getFrameDisplayName(data)).toEqual('exceptions/server{client/city="San Antonio"}'); - expect(data.fields[1].values.length).toEqual(2); - expect(data.fields[0].values.get(0)).toEqual(1504108800000); - expect(data.fields[1].values.get(0)).toEqual(1); - expect(data.fields[0].values.get(1)).toEqual(1504112400000); - expect(data.fields[1].values.get(1)).toEqual(2); - }); - }); - }); - }); - }); - - describe('When getting Metric Names', () => { - const response = { - metrics: { - 'exceptions/server': {}, - 'requests/count': {}, - }, - }; - - beforeEach(() => { - ctx.ds.getResource = jest.fn().mockImplementation((path) => { - expect(path).toContain('/metrics/metadata'); - return Promise.resolve({ data: response, status: 200 }); - }); - }); - - it.skip('should return a list of metric names', () => { - return ctx.ds.getAppInsightsMetricNames().then((results: any) => { - expect(results.length).toBe(2); - expect(results[0].text).toBe('exceptions/server'); - expect(results[0].value).toBe('exceptions/server'); - expect(results[1].text).toBe('requests/count'); - expect(results[1].value).toBe('requests/count'); - }); - }); - }); - - describe('When getting Metric Metadata', () => { - const response = { - metrics: { - 'exceptions/server': { - supportedAggregations: ['sum'], - supportedGroupBy: { - all: ['client/os', 'client/city', 'client/browser'], - }, - defaultAggregation: 'sum', - }, - 'requests/count': { - supportedAggregations: ['avg', 'sum', 'total'], - supportedGroupBy: { - all: ['client/os', 'client/city', 'client/browser'], - }, - defaultAggregation: 'avg', - }, - }, - }; - - beforeEach(() => { - ctx.ds.getResource = jest.fn().mockImplementation((path) => { - expect(path).toContain('/metrics/metadata'); - return Promise.resolve({ data: response, status: 200 }); - }); - }); - - it.skip('should return a list of group bys', () => { - return ctx.ds.getAppInsightsMetricMetadata('requests/count').then((results: any) => { - expect(results.primaryAggType).toEqual('avg'); - expect(results.supportedAggTypes).toContain('avg'); - expect(results.supportedAggTypes).toContain('sum'); - expect(results.supportedAggTypes).toContain('total'); - expect(results.supportedGroupBy).toContain('client/os'); - expect(results.supportedGroupBy).toContain('client/city'); - expect(results.supportedGroupBy).toContain('client/browser'); - }); - }); - }); -}); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/app_insights/app_insights_datasource.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/app_insights/app_insights_datasource.ts deleted file mode 100644 index c94a9dad515..00000000000 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/app_insights/app_insights_datasource.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { isString } from 'lodash'; - -import { DataQueryRequest, DataSourceInstanceSettings, ScopedVars } from '@grafana/data'; -import { DataSourceWithBackend, getTemplateSrv } from '@grafana/runtime'; - -import TimegrainConverter from '../../../time_grain_converter'; -import { - AzureDataSourceJsonData, - AzureMonitorQuery, - DatasourceValidationResult, - DeprecatedAzureQueryType, -} from '../../../types'; -import { routeNames } from '../../../utils/common'; - -import ResponseParser from './response_parser'; - -export interface LogAnalyticsColumn { - text: string; - value: string; -} - -export default class AppInsightsDatasource extends DataSourceWithBackend { - resourcePath: string; - version = 'beta'; - applicationId: string; - logAnalyticsColumns: { [key: string]: LogAnalyticsColumn[] } = {}; - - constructor(instanceSettings: DataSourceInstanceSettings) { - super(instanceSettings); - this.applicationId = instanceSettings.jsonData.appInsightsAppId || ''; - - this.resourcePath = `${routeNames.appInsights}/${this.version}/apps/${this.applicationId}`; - } - - isConfigured(): boolean { - return !!this.applicationId && this.applicationId.length > 0; - } - - createRawQueryRequest(item: any, options: DataQueryRequest, target: AzureMonitorQuery) { - if (item.xaxis && !item.timeColumn) { - item.timeColumn = item.xaxis; - } - - if (item.yaxis && !item.valueColumn) { - item.valueColumn = item.yaxis; - } - - if (item.spliton && !item.segmentColumn) { - item.segmentColumn = item.spliton; - } - - return { - type: 'timeSeriesQuery', - raw: false, - appInsights: { - rawQuery: true, - rawQueryString: getTemplateSrv().replace(item.rawQueryString, options.scopedVars), - timeColumn: item.timeColumn, - valueColumn: item.valueColumn, - segmentColumn: item.segmentColumn, - }, - }; - } - - applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): AzureMonitorQuery { - const item = target.appInsights; - - if (!item) { - return target; - } - - const old: any = item; - // fix for timeGrainUnit which is a deprecated/removed field name - if (old.timeGrainCount) { - item.timeGrain = TimegrainConverter.createISO8601Duration(old.timeGrainCount, item.timeGrainUnit); - } else if (item.timeGrain && item.timeGrainUnit && item.timeGrain !== 'auto') { - item.timeGrain = TimegrainConverter.createISO8601Duration(item.timeGrain, item.timeGrainUnit); - } - - // migration for non-standard names - if (old.groupBy && !item.dimension) { - item.dimension = [old.groupBy]; - } - if (old.filter && !item.dimensionFilter) { - item.dimensionFilter = old.filter; - } - - // Migrate single dimension string to array - if (isString(item.dimension)) { - if (item.dimension === 'None') { - item.dimension = []; - } else { - item.dimension = [item.dimension as string]; - } - } - if (!item.dimension) { - item.dimension = []; - } - - const templateSrv = getTemplateSrv(); - - return { - refId: target.refId, - queryType: DeprecatedAzureQueryType.ApplicationInsights, - appInsights: { - timeGrain: templateSrv.replace((item.timeGrain || '').toString(), scopedVars), - metricName: templateSrv.replace(item.metricName, scopedVars), - aggregation: templateSrv.replace(item.aggregation, scopedVars), - dimension: item.dimension.map((d) => templateSrv.replace(d, scopedVars)), - dimensionFilter: templateSrv.replace(item.dimensionFilter, scopedVars), - alias: item.alias, - }, - }; - } - - testDatasource(): Promise { - const path = `${this.resourcePath}/metrics/metadata`; - return this.getResource(path) - .then((response: any) => { - return { - status: 'success', - message: 'Successfully queried the Application Insights service.', - title: 'Success', - }; - }) - .catch((error: any) => { - let message = 'Application Insights: '; - message += error.statusText ? error.statusText + ': ' : ''; - - if (error.data && error.data.error && error.data.error.code === 'PathNotFoundError') { - message += 'Invalid Application Id for Application Insights service.'; - } else if (error.data && error.data.error) { - message += error.data.error.code + '. ' + error.data.error.message; - } else { - message += 'Cannot connect to Application Insights REST API.'; - } - - return { - status: 'error', - message: message, - }; - }); - } - - getMetricNames() { - const path = `${this.resourcePath}/metrics/metadata`; - return this.getResource(path).then(ResponseParser.parseMetricNames); - } - - getMetricMetadata(metricName: string) { - const path = `${this.resourcePath}/metrics/metadata`; - return this.getResource(path).then((result: any) => { - return new ResponseParser(result).parseMetadata(metricName); - }); - } - - getGroupBys(metricName: string) { - return this.getMetricMetadata(metricName).then((result: any) => { - return new ResponseParser(result).parseGroupBys(); - }); - } - - getQuerySchema() { - const path = `${this.resourcePath}/query/schema`; - return this.getResource(path).then((result: any) => { - const schema = new ResponseParser(result).parseQuerySchema(); - // console.log(schema); - return schema; - }); - } -} diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/app_insights/response_parser.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/app_insights/response_parser.ts deleted file mode 100644 index bdba5d2640c..00000000000 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/app_insights/response_parser.ts +++ /dev/null @@ -1,236 +0,0 @@ -import { concat, filter, find, forEach, indexOf, intersection, isObject, map, without, keys as _keys } from 'lodash'; - -import { dateTime } from '@grafana/data'; - -export default class ResponseParser { - constructor(private results: any) {} - - parseQueryResult() { - let data: any = []; - let columns: any = []; - for (let i = 0; i < this.results.length; i++) { - if (this.results[i].query.raw) { - const xaxis = this.results[i].query.xaxis; - const yaxises = this.results[i].query.yaxis; - const spliton = this.results[i].query.spliton; - columns = this.results[i].result.Tables[0].Columns; - const rows = this.results[i].result.Tables[0].Rows; - data = concat(data, this.parseRawQueryResultRow(this.results[i].query, columns, rows, xaxis, yaxises, spliton)); - } else { - const value = this.results[i].result.value; - const alias = this.results[i].query.alias; - data = concat(data, this.parseQueryResultRow(this.results[i].query, value, alias)); - } - } - return data; - } - - parseRawQueryResultRow(query: any, columns: any, rows: any, xaxis: string, yaxises: string, spliton: string) { - const data: any[] = []; - const columnsForDropdown = map(columns, (column) => ({ text: column.ColumnName, value: column.ColumnName })); - - const xaxisColumn = columns.findIndex((column: any) => column.ColumnName === xaxis); - const yaxisesSplit = yaxises.split(','); - const yaxisColumns: any = {}; - forEach(yaxisesSplit, (yaxis) => { - yaxisColumns[yaxis] = columns.findIndex((column: any) => column.ColumnName === yaxis); - }); - const splitonColumn = columns.findIndex((column: any) => column.ColumnName === spliton); - const convertTimestamp = xaxis === 'timestamp'; - - forEach(rows, (row) => { - forEach(yaxisColumns, (yaxisColumn, yaxisName) => { - const bucket = - splitonColumn === -1 - ? ResponseParser.findOrCreateBucket(data, yaxisName) - : ResponseParser.findOrCreateBucket(data, row[splitonColumn]); - const epoch = convertTimestamp ? ResponseParser.dateTimeToEpoch(row[xaxisColumn]) : row[xaxisColumn]; - bucket.datapoints.push([row[yaxisColumn], epoch]); - bucket.refId = query.refId; - bucket.query = query.query; - bucket.columnsForDropdown = columnsForDropdown; - }); - }); - - return data; - } - - parseQueryResultRow(query: any, value: any, alias: string) { - const data: any[] = []; - - if (ResponseParser.isSingleValue(value)) { - const metricName = ResponseParser.getMetricFieldKey(value); - const aggField = ResponseParser.getKeyForAggregationField(value[metricName]); - const epoch = ResponseParser.dateTimeToEpoch(value.end); - data.push({ - target: metricName, - datapoints: [[value[metricName][aggField], epoch]], - refId: query.refId, - query: query.query, - }); - return data; - } - - const groupedBy = ResponseParser.hasSegmentsField(value.segments[0]); - if (!groupedBy) { - const metricName = ResponseParser.getMetricFieldKey(value.segments[0]); - const dataTarget = ResponseParser.findOrCreateBucket(data, metricName); - - for (let i = 0; i < value.segments.length; i++) { - const epoch = ResponseParser.dateTimeToEpoch(value.segments[i].end); - const aggField: string = ResponseParser.getKeyForAggregationField(value.segments[i][metricName]); - - dataTarget.datapoints.push([value.segments[i][metricName][aggField], epoch]); - } - dataTarget.refId = query.refId; - dataTarget.query = query.query; - } else { - for (let i = 0; i < value.segments.length; i++) { - const epoch = ResponseParser.dateTimeToEpoch(value.segments[i].end); - - for (let j = 0; j < value.segments[i].segments.length; j++) { - const metricName = ResponseParser.getMetricFieldKey(value.segments[i].segments[j]); - const aggField = ResponseParser.getKeyForAggregationField(value.segments[i].segments[j][metricName]); - const target = this.getTargetName(value.segments[i].segments[j], alias); - - const bucket = ResponseParser.findOrCreateBucket(data, target); - bucket.datapoints.push([value.segments[i].segments[j][metricName][aggField], epoch]); - bucket.refId = query.refId; - bucket.meta = { - query: query.query, - }; - } - } - } - - return data; - } - - getTargetName(segment: { [x: string]: string }, alias: string) { - let metric = ''; - let segmentName = ''; - let segmentValue = ''; - for (const prop in segment) { - if (isObject(segment[prop])) { - metric = prop; - } else { - segmentName = prop; - segmentValue = segment[prop]; - } - } - - if (alias) { - const regex = /\{\{([\s\S]+?)\}\}/g; - return alias.replace(regex, (match, g1, g2) => { - const group = g1 || g2; - - if (group === 'metric') { - return metric; - } else if (group === 'groupbyname') { - return segmentName; - } else if (group === 'groupbyvalue') { - return segmentValue; - } - - return match; - }); - } - - return metric + `{${segmentName}="${segmentValue}"}`; - } - - static isSingleValue(value: any) { - return !ResponseParser.hasSegmentsField(value); - } - - static findOrCreateBucket(data: any[], target: string) { - let dataTarget: any = find(data, ['target', target]); - if (!dataTarget) { - dataTarget = { target: target, datapoints: [] }; - data.push(dataTarget); - } - - return dataTarget; - } - - static hasSegmentsField(obj: any) { - const keys = _keys(obj); - return indexOf(keys, 'segments') > -1; - } - - static getMetricFieldKey(segment: { [x: string]: any }) { - const keys = _keys(segment); - - return filter(without(keys, 'start', 'end'), (key) => { - return isObject(segment[key]); - })[0]; - } - - static getKeyForAggregationField(dataObj: any): string { - const keys = _keys(dataObj); - return intersection(keys, ['sum', 'avg', 'min', 'max', 'count', 'unique'])[0]; - } - - static dateTimeToEpoch(dateTimeValue: any) { - return dateTime(dateTimeValue).valueOf(); - } - - static parseMetricNames(result: { metrics: any }) { - const keys = _keys(result.metrics); - - return ResponseParser.toTextValueList(keys); - } - - parseMetadata(metricName: string) { - const metric = this.results.metrics[metricName]; - - if (!metric) { - throw Error('No data found for metric: ' + metricName); - } - - return { - primaryAggType: metric.defaultAggregation, - supportedAggTypes: metric.supportedAggregations, - supportedGroupBy: metric.supportedGroupBy.all, - }; - } - - parseGroupBys() { - return ResponseParser.toTextValueList(this.results.supportedGroupBy); - } - - parseQuerySchema() { - const result: any = { - Type: 'AppInsights', - Tables: {}, - }; - if (this.results && this.results && this.results.Tables) { - for (let i = 0; i < this.results.Tables[0].Rows.length; i++) { - const column = this.results.Tables[0].Rows[i]; - const columnTable = column[0]; - const columnName = column[1]; - const columnType = column[2]; - if (result.Tables[columnTable]) { - result.Tables[columnTable].OrderedColumns.push({ Name: columnName, Type: columnType }); - } else { - result.Tables[columnTable] = { - Name: columnTable, - OrderedColumns: [{ Name: columnName, Type: columnType }], - }; - } - } - } - return result; - } - - static toTextValueList(values: any) { - const list: any[] = []; - for (let i = 0; i < values.length; i++) { - list.push({ - text: values[i], - value: values[i], - }); - } - return list; - } -} diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/AnalyticsConfig.test.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/AnalyticsConfig.test.tsx deleted file mode 100644 index 9528c74900a..00000000000 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/AnalyticsConfig.test.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; - -import AnalyticsConfig, { Props } from './AnalyticsConfig'; - -const setup = (propsFunc?: (props: Props) => Props) => { - let props: Props = { - options: { - id: 21, - uid: 'x', - orgId: 1, - name: 'Azure Monitor-10-10', - type: 'grafana-azure-monitor-datasource', - typeName: 'Azure', - typeLogoUrl: '', - access: 'proxy', - url: '', - password: '', - user: '', - database: '', - basicAuth: false, - basicAuthUser: '', - basicAuthPassword: '', - withCredentials: false, - isDefault: false, - secureJsonFields: {}, - jsonData: { - cloudName: '', - subscriptionId: '', - }, - version: 1, - readOnly: false, - }, - updateOptions: jest.fn(), - }; - - if (propsFunc) { - props = propsFunc(props); - } - - return render(); -}; - -describe('Render', () => { - it('should disable log analytics credentials form', () => { - setup((props) => ({ - ...props, - options: { - ...props.options, - jsonData: { - ...props.options.jsonData, - azureLogAnalyticsSameAs: true, - }, - }, - })); - expect(screen.queryByText('Azure Monitor Logs')).not.toBeInTheDocument(); - }); - - it('should not render the Switch to use different creds for log analytics by default', () => { - setup(); - expect(screen.queryByText('is no longer supported', { exact: false })).not.toBeInTheDocument(); - }); - - // Remove this test with deprecated code - it('should not render the Switch if different creds for log analytics were set from before', () => { - setup((props) => ({ - ...props, - options: { - ...props.options, - jsonData: { - ...props.options.jsonData, - azureLogAnalyticsSameAs: false, - }, - }, - })); - expect(screen.queryByText('is no longer supported', { exact: false })).toBeInTheDocument(); - }); - - it('should clean up the error when resetting the credentials', async () => { - const onUpdate = jest.fn(); - setup((props) => ({ - ...props, - options: { - ...props.options, - jsonData: { - ...props.options.jsonData, - azureLogAnalyticsSameAs: false, - }, - }, - updateOptions: onUpdate, - })); - expect(screen.queryByText('is no longer supported', { exact: false })).toBeInTheDocument(); - await userEvent.click(screen.getByText('Clear Azure Monitor Logs Credentials')); - expect(onUpdate).toHaveBeenCalled(); - const newOpts = onUpdate.mock.calls[0][0]({}); - expect(newOpts).toEqual({ jsonData: { azureLogAnalyticsSameAs: true } }); - }); -}); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/AnalyticsConfig.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/AnalyticsConfig.tsx deleted file mode 100644 index b3276e4ab78..00000000000 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/AnalyticsConfig.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React, { FunctionComponent, useMemo } from 'react'; - -import { Alert, Button } from '@grafana/ui'; - -import { getCredentials } from '../../../credentials'; -import { AzureDataSourceSettings } from '../../../types'; -import { AzureCredentialsForm } from '../../AzureCredentialsForm'; - -export interface Props { - options: AzureDataSourceSettings; - updateOptions: (optionsFunc: (options: AzureDataSourceSettings) => AzureDataSourceSettings) => void; -} - -export const AnalyticsConfig: FunctionComponent = (props: Props) => { - const { updateOptions } = props; - const primaryCredentials = useMemo(() => getCredentials(props.options), [props.options]); - - // Only show a section for setting LogAnalytics credentials if - // they were set from before with different values and the - // authType is supported - const logCredentialsEnabled = - primaryCredentials.authType === 'clientsecret' && props.options.jsonData.azureLogAnalyticsSameAs === false; - - const onClearAzLogsCreds = () => { - updateOptions((options) => { - return { - ...options, - jsonData: { - ...options.jsonData, - azureLogAnalyticsSameAs: true, - }, - }; - }); - }; - - return logCredentialsEnabled ? ( - <> -

Azure Monitor Logs

- <> - - Using different credentials for Azure Monitor Logs is no longer supported. Authentication information above - will be used instead. Please create a new data source with the credentials below. - - - - - - - - ) : null; -}; - -export default AnalyticsConfig; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/ApplicationInsightsEditor/index.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/ApplicationInsightsEditor/index.tsx deleted file mode 100644 index 51adf3971f4..00000000000 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/ApplicationInsightsEditor/index.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; - -import { Alert, Input } from '@grafana/ui'; - -import { Field } from '../../../Field'; -import { DeprecatedAzureMonitorQuery } from '../../types'; - -const ReadOnlyTimeGrain = ({ - timeGrainCount, - timeGrainType, - timeGrainUnit, -}: { - timeGrainCount: string; - timeGrainType: string; - timeGrainUnit: string; -}) => { - const timeFields = timeGrainType === 'specific' ? ['specific', timeGrainCount, timeGrainUnit] : [timeGrainType]; - - return ( - - <> - {timeFields.map((timeField) => ( - {}} key={timeField} width={10} /> - ))} - - - ); -}; - -const ApplicationInsightsEditor = ({ query }: { query: DeprecatedAzureMonitorQuery }) => { - const groupBy = query.appInsights?.dimension || []; - - return ( -
- - {}} - id="azure-monitor-application-insights-metric" - /> - - - {}} /> - - {groupBy.length > 0 && ( - - <> - {groupBy.map((dimension) => ( - {}} key={dimension} /> - ))} - - - )} - - {}} /> - - - - {}} disabled={true} /> - - - Application Insights is deprecated and is now read only. Migrate your queries to Metrics to make changes. - -
- ); -}; - -export default ApplicationInsightsEditor; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/InsightsAnalyticsEditor/index.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/InsightsAnalyticsEditor/index.tsx deleted file mode 100644 index e1a69edfc5e..00000000000 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/InsightsAnalyticsEditor/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; - -import { Alert, CodeEditor, Select } from '@grafana/ui'; - -import { AzureMonitorOption } from '../../../../types'; -import { Field } from '../../../Field'; -import { Space } from '../../../Space'; -import { DeprecatedAzureMonitorQuery } from '../../types'; - -interface InsightsAnalyticsEditorProps { - query: DeprecatedAzureMonitorQuery; -} - -const FORMAT_OPTIONS: Array> = [ - { label: 'Time series', value: 'time_series' }, - { label: 'Table', value: 'table' }, -]; - -const InsightsAnalyticsEditor: React.FC = ({ query }) => { - return ( -
- - - - -
-
-
- -
-
- - ) : ( -
-
- API Key -
- -
-
-
- )} -
-
- Application ID -
- -
-
-
- - - ); - } -} - -export default InsightsConfig; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/__snapshots__/InsightsConfig.test.tsx.snap b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/__snapshots__/InsightsConfig.test.tsx.snap deleted file mode 100644 index 4685035e90f..00000000000 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/components/__snapshots__/InsightsConfig.test.tsx.snap +++ /dev/null @@ -1,208 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Render should disable insights api key input 1`] = ` - -

- Azure Application Insights -

- - Configure using Azure AD App Registration above and update existing queries to use Metrics or Logs. - -
-
-
- - API Key - - -
-
-
- -
-
-
-
-
- - Application ID - -
- -
-
-
-
-
-`; - -exports[`Render should enable insights api key input 1`] = ` - -

- Azure Application Insights -

- - Configure using Azure AD App Registration above and update existing queries to use Metrics or Logs. - -
-
-
- - API Key - -
- -
-
-
-
-
- - Application ID - -
- -
-
-
-
-
-`; - -exports[`Render should render component 1`] = ` - -

- Azure Application Insights -

- - Configure using Azure AD App Registration above and update existing queries to use Metrics or Logs. - -
-
-
- - API Key - -
- -
-
-
-
-
- - Application ID - -
- -
-
-
-
-
-`; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/insights_analytics/insights_analytics_datasource.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/insights_analytics/insights_analytics_datasource.ts deleted file mode 100644 index 32086656594..00000000000 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/insights_analytics/insights_analytics_datasource.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { DataSourceInstanceSettings, ScopedVars } from '@grafana/data'; -import { getTemplateSrv } from '@grafana/runtime'; - -import { AzureDataSourceJsonData, DeprecatedAzureQueryType } from '../../../types'; -import AppInsightsDatasource from '../app_insights/app_insights_datasource'; -import { DeprecatedAzureMonitorQuery } from '../types'; - -export default class InsightsAnalyticsDatasource extends AppInsightsDatasource { - constructor(instanceSettings: DataSourceInstanceSettings) { - super(instanceSettings); - } - - applyTemplateVariables(target: DeprecatedAzureMonitorQuery, scopedVars: ScopedVars): DeprecatedAzureMonitorQuery { - const item = target.insightsAnalytics; - if (!item) { - return target; - } - - const query = item.rawQueryString && !item.query ? item.rawQueryString : item.query; - - return { - refId: target.refId, - queryType: DeprecatedAzureQueryType.InsightsAnalytics, - insightsAnalytics: { - query: getTemplateSrv().replace(query, scopedVars), - resultFormat: item.resultFormat, - }, - }; - } -} diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/types/index.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/types/index.ts deleted file mode 100644 index a38910e42af..00000000000 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/types/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './query'; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/types/query.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/types/query.ts deleted file mode 100644 index d8f8fe15235..00000000000 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/types/query.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { DataQuery } from '@grafana/data'; - -import { - AzureLogsQuery, - AzureMetricQuery, - AzureQueryType, - AzureResourceGraphQuery, - DeprecatedAzureQueryType, -} from '../../../types'; -import { GrafanaTemplateVariableQuery } from '../../../types/templateVariables'; - -export interface DeprecatedAzureMonitorQuery extends DataQuery { - queryType?: AzureQueryType | DeprecatedAzureQueryType; - - subscription?: string; - - /** ARG uses multiple subscriptions */ - subscriptions?: string[]; - - azureMonitor?: AzureMetricQuery; - azureLogAnalytics?: AzureLogsQuery; - azureResourceGraph?: AzureResourceGraphQuery; - grafanaTemplateVariableFn?: GrafanaTemplateVariableQuery; - - /** @deprecated App Insights/Insights Analytics deprecated in v8 */ - appInsights?: ApplicationInsightsQuery; - - /** @deprecated App Insights/Insights Analytics deprecated in v8 */ - insightsAnalytics?: InsightsAnalyticsQuery; -} - -/** - * Azure Monitor App Insights sub-query properties - * @deprecated App Insights deprecated in v8 in favor of Metrics queries - */ -export interface ApplicationInsightsQuery { - metricName?: string; - timeGrain?: string; - timeGrainCount?: string; - timeGrainType?: string; - timeGrainUnit?: string; - aggregation?: string; - dimension?: string[]; // Was string before 7.1 - dimensionFilter?: string; - alias?: string; - - /** @deprecated Migrated to Insights Analytics query */ - rawQuery?: string; -} - -/** - * Azure Monitor Insights Analytics sub-query properties - * @deprecated Insights Analytics deprecated in v8 in favor of Logs queries - */ -export interface InsightsAnalyticsQuery { - query?: string; - resultFormat?: string; - - /** @deprecated Migrate field to query */ - rawQueryString?: string; -} diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/utils.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/utils.tsx deleted file mode 100644 index 6c0e96bd4cd..00000000000 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/components/deprecated/utils.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { gt, valid } from 'semver'; - -import { config } from '@grafana/runtime'; - -import { AzureDataSourceSettings } from '../../types'; - -export function isAppInsightsConfigured(options: AzureDataSourceSettings) { - return !!(options.jsonData.appInsightsAppId && options.secureJsonFields.appInsightsApiKey); -} - -export function gtGrafana9() { - // AppInsights configuration will be removed with Grafana 9 - return valid(config.buildInfo.version) && gt(config.buildInfo.version, '9.0.0-beta1'); -} diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/datasource.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/datasource.ts index 81ea72a8997..59ebd9d1bc0 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/datasource.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/datasource.ts @@ -16,18 +16,8 @@ import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_sr import AzureLogAnalyticsDatasource from './azure_log_analytics/azure_log_analytics_datasource'; import AzureMonitorDatasource from './azure_monitor/azure_monitor_datasource'; import AzureResourceGraphDatasource from './azure_resource_graph/azure_resource_graph_datasource'; -import AppInsightsDatasource from './components/deprecated/app_insights/app_insights_datasource'; -import InsightsAnalyticsDatasource from './components/deprecated/insights_analytics/insights_analytics_datasource'; -import { gtGrafana9 } from './components/deprecated/utils'; -import { getAzureCloud } from './credentials'; import ResourcePickerData from './resourcePicker/resourcePickerData'; -import { - AzureDataSourceJsonData, - AzureMonitorQuery, - AzureQueryType, - DatasourceValidationResult, - DeprecatedAzureQueryType, -} from './types'; +import { AzureDataSourceJsonData, AzureMonitorQuery, AzureQueryType, DatasourceValidationResult } from './types'; import migrateAnnotation from './utils/migrateAnnotation'; import { datasourceMigrations } from './utils/migrateQuery'; import { VariableSupport } from './variables'; @@ -41,18 +31,9 @@ export default class Datasource extends DataSourceApi; @@ -73,15 +54,6 @@ export default class Datasource extends DataSourceApi): Observable { - const byType = new Map>(); + const byType = new Map>(); for (const baseTarget of options.targets) { // Migrate old query structures @@ -176,10 +148,6 @@ export default class Datasource extends DataSourceApi { let status: 'success' | 'error' = 'success'; let message = ''; @@ -219,19 +187,6 @@ export default class Datasource extends DataSourceApi //the table to query (e.g. Usage, Heartbeat, Perf)\n| where $__timeFilter(TimeGenerated) //this is a macro used to show the full chart’s time range, choose the datetime column here\n| summarize count() by , bin(TimeGenerated, $__interval) //change “group by column” to a column in your table, such as “Computer”. The $__interval macro is used to auto-select the time grain. Can also use 1h, 5m etc.\n| order by TimeGenerated asc', @@ -75,7 +74,6 @@ const modernMetricsQuery: AzureMonitorQuery = { top: '10', }, azureResourceGraph: { resultFormat: 'table' }, - insightsAnalytics: { query: '', resultFormat: 'time_series' }, queryType: AzureQueryType.AzureMonitor, refId: 'A', subscription: '44693801-6ee6-49de-9b2d-9106972f9572', diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/migrateQuery.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/migrateQuery.ts index 1c55b1c5b5e..a0b817f935d 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/migrateQuery.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/migrateQuery.ts @@ -5,7 +5,7 @@ import { setTimeGrain as setMetricsTimeGrain, } from '../components/MetricsQueryEditor/setQueryValue'; import TimegrainConverter from '../time_grain_converter'; -import { AzureMonitorQuery, AzureQueryType, DeprecatedAzureQueryType } from '../types'; +import { AzureMonitorQuery, AzureQueryType } from '../types'; const OLD_DEFAULT_DROPDOWN_VALUE = 'select'; @@ -20,7 +20,6 @@ export default function migrateQuery(query: AzureMonitorQuery): AzureMonitorQuer workingQuery = migrateTimeGrains(workingQuery); workingQuery = migrateLogAnalyticsToFromTimes(workingQuery); workingQuery = migrateToDefaultNamespace(workingQuery); - workingQuery = migrateApplicationInsightsDimensions(workingQuery); workingQuery = migrateMetricsDimensionFilters(workingQuery); workingQuery = migrateResourceUri(workingQuery); @@ -40,33 +39,6 @@ function migrateTimeGrains(query: AzureMonitorQuery): AzureMonitorQuery { delete workingQuery.azureMonitor?.timeGrainUnit; } - if (workingQuery.appInsights?.timeGrainUnit && workingQuery.appInsights.timeGrain !== 'auto') { - const appInsights = { - ...workingQuery.appInsights, - }; - - if (workingQuery.appInsights.timeGrainCount) { - appInsights.timeGrain = TimegrainConverter.createISO8601Duration( - workingQuery.appInsights.timeGrainCount, - workingQuery.appInsights.timeGrainUnit - ); - } else { - appInsights.timeGrainCount = workingQuery.appInsights.timeGrain; - - if (workingQuery.appInsights.timeGrain) { - appInsights.timeGrain = TimegrainConverter.createISO8601Duration( - workingQuery.appInsights.timeGrain, - workingQuery.appInsights.timeGrainUnit - ); - } - } - - workingQuery = { - ...workingQuery, - appInsights: appInsights, - }; - } - return workingQuery; } @@ -107,22 +79,6 @@ function migrateToDefaultNamespace(query: AzureMonitorQuery): AzureMonitorQuery return query; } -function migrateApplicationInsightsDimensions(query: AzureMonitorQuery): AzureMonitorQuery { - const dimension = query?.appInsights?.dimension as unknown; - - if (dimension && typeof dimension === 'string') { - return { - ...query, - appInsights: { - ...query.appInsights, - dimension: [dimension], - }, - }; - } - - return query; -} - function migrateMetricsDimensionFilters(query: AzureMonitorQuery): AzureMonitorQuery { let workingQuery = query; @@ -166,18 +122,6 @@ function migrateResourceUri(query: AzureMonitorQuery): AzureMonitorQuery { export function datasourceMigrations(query: AzureMonitorQuery): AzureMonitorQuery { let workingQuery = query; - if (workingQuery.queryType === DeprecatedAzureQueryType.ApplicationInsights && workingQuery.appInsights?.rawQuery) { - workingQuery = { - ...workingQuery, - queryType: DeprecatedAzureQueryType.InsightsAnalytics, - appInsights: undefined, - insightsAnalytics: { - query: workingQuery.appInsights.rawQuery, - resultFormat: 'time_series', - }, - }; - } - if (!workingQuery.queryType) { workingQuery = { ...workingQuery, diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.test.ts index 21518be17c9..ed5ce12ce7d 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.test.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.test.ts @@ -17,62 +17,6 @@ jest.mock('@grafana/runtime', () => ({ })); describe('VariableSupport', () => { describe('querying for grafana template variable fns', () => { - it('can fetch deprecated log analytics metric names', (done) => { - const expectedResults = ['test']; - const variableSupport = new VariableSupport( - createMockDatasource({ - insightsAnalyticsDatasource: { - getMetricNames: jest.fn().mockResolvedValueOnce(expectedResults), - }, - }) - ); - const mockRequest = { - targets: [ - { - refId: 'A', - queryType: AzureQueryType.GrafanaTemplateVariableFn, - grafanaTemplateVariableFn: { - kind: 'AppInsightsMetricNameQuery', - rawQuery: 'AppInsightsMetricNames()', - }, - } as AzureMonitorQuery, - ], - } as DataQueryRequest; - const observables = variableSupport.query(mockRequest); - observables.subscribe((result: DataQueryResponseData) => { - expect(result.data[0].source).toEqual(expectedResults); - done(); - }); - }); - - it('can fetch deprecated log analytics groupBys', (done) => { - const expectedResults = ['test']; - const variableSupport = new VariableSupport( - createMockDatasource({ - insightsAnalyticsDatasource: { - getGroupBys: jest.fn().mockResolvedValueOnce(expectedResults), - }, - }) - ); - const mockRequest = { - targets: [ - { - refId: 'A', - queryType: AzureQueryType.GrafanaTemplateVariableFn, - grafanaTemplateVariableFn: { - kind: 'AppInsightsGroupByQuery', - rawQuery: 'AppInsightsGroupBys(metricname)', - }, - } as AzureMonitorQuery, - ], - } as DataQueryRequest; - const observables = variableSupport.query(mockRequest); - observables.subscribe((result: DataQueryResponseData) => { - expect(result.data[0].source).toEqual(expectedResults); - done(); - }); - }); - it('can fetch subscriptions', (done) => { const fakeSubscriptions = ['subscriptionId']; const variableSupport = new VariableSupport( diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.ts index c544ba116c7..bf49cad6800 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/variables.ts @@ -47,17 +47,6 @@ export class VariableSupport extends CustomVariableSupport | null { - // deprecated app insights template variables (will most likely remove in grafana 9) - if (this.datasource.insightsAnalyticsDatasource) { - if (query.kind === 'AppInsightsMetricNameQuery') { - return this.datasource.insightsAnalyticsDatasource.getMetricNames(); - } - - if (query.kind === 'AppInsightsGroupByQuery') { - return this.datasource.insightsAnalyticsDatasource.getGroupBys(getTemplateSrv().replace(query.metricName)); - } - } - if (query.kind === 'SubscriptionsQuery') { return this.datasource.getSubscriptions(); }