mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AzureMonitor: Fix Log Analytics portal links (#65482)
* Fix and update Log Analytics portal links - Build portal URL in backend - Correctly set multiple resource value - Move AddConfigLinks util function - Add necessary types - Remove unused functions * Fix lint issue * Remove unused cache variable
This commit is contained in:
parent
674144c8e8
commit
e27cb67776
@ -3853,10 +3853,7 @@ exports[`better eslint`] = {
|
|||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "6"]
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
|
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
|
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "9"]
|
|
||||||
],
|
],
|
||||||
"public/app/plugins/datasource/azuremonitor/azure_log_analytics/response_parser.ts:5381": [
|
"public/app/plugins/datasource/azuremonitor/azure_log_analytics/response_parser.ts:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||||
|
@ -4,10 +4,12 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
@ -16,7 +18,6 @@ import (
|
|||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/macros"
|
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/macros"
|
||||||
@ -204,17 +205,16 @@ func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, logger l
|
|||||||
return dataResponse
|
return dataResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
model, err := simplejson.NewJson(query.JSON)
|
azurePortalBaseUrl, err := GetAzurePortalUrl(dsInfo.Cloud)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dataResponseErrorWithExecuted(err)
|
dataResponse.Error = err
|
||||||
|
return dataResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
err = setAdditionalFrameMeta(frame,
|
queryUrl, err := getQueryUrl(query.Query, query.Resources, azurePortalBaseUrl)
|
||||||
query.Query,
|
|
||||||
model.Get("azureLogAnalytics").Get("resource").MustString())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
frame.AppendNotices(data.Notice{Severity: data.NoticeSeverityWarning, Text: "could not add custom metadata: " + err.Error()})
|
dataResponse.Error = err
|
||||||
logger.Warn("failed to add custom metadata to azure log analytics response", err)
|
return dataResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
if query.ResultFormat == types.TimeSeries {
|
if query.ResultFormat == types.TimeSeries {
|
||||||
@ -229,6 +229,8 @@ func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, logger l
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AddConfigLinks(*frame, queryUrl)
|
||||||
|
|
||||||
dataResponse.Frames = data.Frames{frame}
|
dataResponse.Frames = data.Frames{frame}
|
||||||
return dataResponse
|
return dataResponse
|
||||||
}
|
}
|
||||||
@ -268,6 +270,43 @@ func (e *AzureLogAnalyticsDatasource) createRequest(ctx context.Context, logger
|
|||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AzureLogAnalyticsURLResources struct {
|
||||||
|
Resources []AzureLogAnalyticsURLResource `json:"resources"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AzureLogAnalyticsURLResource struct {
|
||||||
|
ResourceID string `json:"resourceId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getQueryUrl(query string, resources []string, azurePortalUrl string) (string, error) {
|
||||||
|
encodedQuery, err := encodeQuery(query)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to encode the query: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
portalUrl := azurePortalUrl
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to parse base portal URL: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
portalUrl += "/#blade/Microsoft_OperationsManagementSuite_Workspace/AnalyticsBlade/initiator/AnalyticsShareLinkToQuery/isQueryEditorVisible/true/scope/"
|
||||||
|
resourcesJson := AzureLogAnalyticsURLResources{
|
||||||
|
Resources: make([]AzureLogAnalyticsURLResource, 0),
|
||||||
|
}
|
||||||
|
for _, resource := range resources {
|
||||||
|
resourcesJson.Resources = append(resourcesJson.Resources, AzureLogAnalyticsURLResource{
|
||||||
|
ResourceID: resource,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
resourcesMarshalled, err := json.Marshal(resourcesJson)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to marshal log analytics resources: %s", err)
|
||||||
|
}
|
||||||
|
portalUrl += url.QueryEscape(string(resourcesMarshalled))
|
||||||
|
portalUrl += "/query/" + url.PathEscape(encodedQuery) + "/isQueryBase64Compressed/true/timespanInIsoFormat/P1D"
|
||||||
|
return portalUrl, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Error definition has been inferred from real data and other model definitions like
|
// Error definition has been inferred from real data and other model definitions like
|
||||||
// https://github.com/Azure/azure-sdk-for-go/blob/3640559afddbad452d265b54fb1c20b30be0b062/services/preview/virtualmachineimagebuilder/mgmt/2019-05-01-preview/virtualmachineimagebuilder/models.go
|
// https://github.com/Azure/azure-sdk-for-go/blob/3640559afddbad452d265b54fb1c20b30be0b062/services/preview/virtualmachineimagebuilder/mgmt/2019-05-01-preview/virtualmachineimagebuilder/models.go
|
||||||
type AzureLogAnalyticsAPIError struct {
|
type AzureLogAnalyticsAPIError struct {
|
||||||
@ -336,41 +375,20 @@ func (e *AzureLogAnalyticsDatasource) unmarshalResponse(logger log.Logger, res *
|
|||||||
|
|
||||||
// LogAnalyticsMeta is a type for the a Frame's Meta's Custom property.
|
// LogAnalyticsMeta is a type for the a Frame's Meta's Custom property.
|
||||||
type LogAnalyticsMeta struct {
|
type LogAnalyticsMeta struct {
|
||||||
ColumnTypes []string `json:"azureColumnTypes"`
|
ColumnTypes []string `json:"azureColumnTypes"`
|
||||||
EncodedQuery []byte `json:"encodedQuery"` // EncodedQuery is used for deep links.
|
|
||||||
Resource string `json:"resource"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func setAdditionalFrameMeta(frame *data.Frame, query, resource string) error {
|
|
||||||
if frame.Meta == nil || frame.Meta.Custom == nil {
|
|
||||||
// empty response
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
frame.Meta.ExecutedQueryString = query
|
|
||||||
la, ok := frame.Meta.Custom.(*LogAnalyticsMeta)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("unexpected type found for frame's custom metadata")
|
|
||||||
}
|
|
||||||
la.Resource = resource
|
|
||||||
encodedQuery, err := encodeQuery(query)
|
|
||||||
if err == nil {
|
|
||||||
la.EncodedQuery = encodedQuery
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("failed to encode the query into the encodedQuery property")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// encodeQuery encodes the query in gzip so the frontend can build links.
|
// encodeQuery encodes the query in gzip so the frontend can build links.
|
||||||
func encodeQuery(rawQuery string) ([]byte, error) {
|
func encodeQuery(rawQuery string) (string, error) {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
gz := gzip.NewWriter(&b)
|
gz := gzip.NewWriter(&b)
|
||||||
if _, err := gz.Write([]byte(rawQuery)); err != nil {
|
if _, err := gz.Write([]byte(rawQuery)); err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gz.Close(); err != nil {
|
if err := gz.Close(); err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.Bytes(), nil
|
return base64.StdEncoding.EncodeToString(b.Bytes()), nil
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
@ -287,11 +286,3 @@ func Test_executeQueryErrorWithDifferentLogAnalyticsCreds(t *testing.T) {
|
|||||||
t.Error("expecting the error to inform of bad credentials")
|
t.Error("expecting the error to inform of bad credentials")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_setAdditionalFrameMeta(t *testing.T) {
|
|
||||||
t.Run("it should not error with an empty response", func(t *testing.T) {
|
|
||||||
frame := data.NewFrame("test")
|
|
||||||
err := setAdditionalFrameMeta(frame, "", "")
|
|
||||||
require.NoError(t, err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
36
pkg/tsdb/azuremonitor/loganalytics/utils.go
Normal file
36
pkg/tsdb/azuremonitor/loganalytics/utils.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package loganalytics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana-azure-sdk-go/azsettings"
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AddConfigLinks(frame data.Frame, dl string) data.Frame {
|
||||||
|
for i := range frame.Fields {
|
||||||
|
if frame.Fields[i].Config == nil {
|
||||||
|
frame.Fields[i].Config = &data.FieldConfig{}
|
||||||
|
}
|
||||||
|
deepLink := data.DataLink{
|
||||||
|
Title: "View in Azure Portal",
|
||||||
|
TargetBlank: true,
|
||||||
|
URL: dl,
|
||||||
|
}
|
||||||
|
frame.Fields[i].Config.Links = append(frame.Fields[i].Config.Links, deepLink)
|
||||||
|
}
|
||||||
|
return frame
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAzurePortalUrl(azureCloud string) (string, error) {
|
||||||
|
switch azureCloud {
|
||||||
|
case azsettings.AzurePublic:
|
||||||
|
return "https://portal.azure.com", nil
|
||||||
|
case azsettings.AzureChina:
|
||||||
|
return "https://portal.azure.cn", nil
|
||||||
|
case azsettings.AzureUSGovernment:
|
||||||
|
return "https://portal.azure.us", nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("the cloud is not supported")
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/resourcegraph"
|
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/loganalytics"
|
||||||
azTime "github.com/grafana/grafana/pkg/tsdb/azuremonitor/time"
|
azTime "github.com/grafana/grafana/pkg/tsdb/azuremonitor/time"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/types"
|
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/types"
|
||||||
)
|
)
|
||||||
@ -246,7 +246,7 @@ func (e *AzureMonitorDatasource) executeQuery(ctx context.Context, logger log.Lo
|
|||||||
return dataResponse
|
return dataResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
azurePortalUrl, err := resourcegraph.GetAzurePortalUrl(dsInfo.Cloud)
|
azurePortalUrl, err := loganalytics.GetAzurePortalUrl(dsInfo.Cloud)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dataResponse.Error = err
|
dataResponse.Error = err
|
||||||
return dataResponse
|
return dataResponse
|
||||||
@ -379,7 +379,7 @@ func (e *AzureMonitorDatasource) parseResponse(amr types.AzureMonitorResponse, q
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
frameWithLink := resourcegraph.AddConfigLinks(*frame, queryUrl)
|
frameWithLink := loganalytics.AddConfigLinks(*frame, queryUrl)
|
||||||
frames = append(frames, &frameWithLink)
|
frames = append(frames, &frameWithLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana-azure-sdk-go/azsettings"
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
@ -203,13 +202,13 @@ func (e *AzureResourceGraphDatasource) executeQuery(ctx context.Context, logger
|
|||||||
return dataResponse
|
return dataResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
azurePortalUrl, err := GetAzurePortalUrl(dsInfo.Cloud)
|
azurePortalUrl, err := loganalytics.GetAzurePortalUrl(dsInfo.Cloud)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dataResponseErrorWithExecuted(err)
|
return dataResponseErrorWithExecuted(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
url := azurePortalUrl + "/#blade/HubsExtension/ArgQueryBlade/query/" + url.PathEscape(query.InterpolatedQuery)
|
url := azurePortalUrl + "/#blade/HubsExtension/ArgQueryBlade/query/" + url.PathEscape(query.InterpolatedQuery)
|
||||||
frameWithLink := AddConfigLinks(*frame, url)
|
frameWithLink := loganalytics.AddConfigLinks(*frame, url)
|
||||||
if frameWithLink.Meta == nil {
|
if frameWithLink.Meta == nil {
|
||||||
frameWithLink.Meta = &data.FrameMeta{}
|
frameWithLink.Meta = &data.FrameMeta{}
|
||||||
}
|
}
|
||||||
@ -219,21 +218,6 @@ func (e *AzureResourceGraphDatasource) executeQuery(ctx context.Context, logger
|
|||||||
return dataResponse
|
return dataResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddConfigLinks(frame data.Frame, dl string) data.Frame {
|
|
||||||
for i := range frame.Fields {
|
|
||||||
if frame.Fields[i].Config == nil {
|
|
||||||
frame.Fields[i].Config = &data.FieldConfig{}
|
|
||||||
}
|
|
||||||
deepLink := data.DataLink{
|
|
||||||
Title: "View in Azure Portal",
|
|
||||||
TargetBlank: true,
|
|
||||||
URL: dl,
|
|
||||||
}
|
|
||||||
frame.Fields[i].Config.Links = append(frame.Fields[i].Config.Links, deepLink)
|
|
||||||
}
|
|
||||||
return frame
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *AzureResourceGraphDatasource) createRequest(ctx context.Context, logger log.Logger, reqBody []byte, url string) (*http.Request, error) {
|
func (e *AzureResourceGraphDatasource) createRequest(ctx context.Context, logger log.Logger, reqBody []byte, url string) (*http.Request, error) {
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(reqBody))
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(reqBody))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -273,16 +257,3 @@ func (e *AzureResourceGraphDatasource) unmarshalResponse(logger log.Logger, res
|
|||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAzurePortalUrl(azureCloud string) (string, error) {
|
|
||||||
switch azureCloud {
|
|
||||||
case azsettings.AzurePublic:
|
|
||||||
return "https://portal.azure.com", nil
|
|
||||||
case azsettings.AzureChina:
|
|
||||||
return "https://portal.azure.cn", nil
|
|
||||||
case azsettings.AzureUSGovernment:
|
|
||||||
return "https://portal.azure.us", nil
|
|
||||||
default:
|
|
||||||
return "", fmt.Errorf("the cloud is not supported")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/loganalytics"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/types"
|
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -124,7 +125,7 @@ func TestAddConfigData(t *testing.T) {
|
|||||||
frame := data.Frame{
|
frame := data.Frame{
|
||||||
Fields: []*data.Field{&field},
|
Fields: []*data.Field{&field},
|
||||||
}
|
}
|
||||||
frameWithLink := AddConfigLinks(frame, "http://ds")
|
frameWithLink := loganalytics.AddConfigLinks(frame, "http://ds")
|
||||||
expectedFrameWithLink := data.Frame{
|
expectedFrameWithLink := data.Frame{
|
||||||
Fields: []*data.Field{
|
Fields: []*data.Field{
|
||||||
{
|
{
|
||||||
@ -148,7 +149,7 @@ func TestGetAzurePortalUrl(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, cloud := range clouds {
|
for _, cloud := range clouds {
|
||||||
azurePortalUrl, err := GetAzurePortalUrl(cloud)
|
azurePortalUrl, err := loganalytics.GetAzurePortalUrl(cloud)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("The cloud not supported")
|
t.Errorf("The cloud not supported")
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,6 @@
|
|||||||
import { map } from 'lodash';
|
import { map } from 'lodash';
|
||||||
import { from, Observable } from 'rxjs';
|
|
||||||
import { mergeMap } from 'rxjs/operators';
|
|
||||||
|
|
||||||
import {
|
import { DataSourceInstanceSettings, DataSourceRef, ScopedVars } from '@grafana/data';
|
||||||
DataQueryRequest,
|
|
||||||
DataQueryResponse,
|
|
||||||
DataSourceInstanceSettings,
|
|
||||||
DataSourceRef,
|
|
||||||
ScopedVars,
|
|
||||||
} from '@grafana/data';
|
|
||||||
import { DataSourceWithBackend, getTemplateSrv } from '@grafana/runtime';
|
import { DataSourceWithBackend, getTemplateSrv } from '@grafana/runtime';
|
||||||
|
|
||||||
import { isGUIDish } from '../components/ResourcePicker/utils';
|
import { isGUIDish } from '../components/ResourcePicker/utils';
|
||||||
@ -43,11 +35,9 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
|
|||||||
|
|
||||||
azureMonitorPath: string;
|
azureMonitorPath: string;
|
||||||
firstWorkspace?: string;
|
firstWorkspace?: string;
|
||||||
cache: Map<string, any>;
|
|
||||||
|
|
||||||
constructor(private instanceSettings: DataSourceInstanceSettings<AzureDataSourceJsonData>) {
|
constructor(private instanceSettings: DataSourceInstanceSettings<AzureDataSourceJsonData>) {
|
||||||
super(instanceSettings);
|
super(instanceSettings);
|
||||||
this.cache = new Map();
|
|
||||||
|
|
||||||
this.resourcePath = `${routeNames.logAnalytics}`;
|
this.resourcePath = `${routeNames.logAnalytics}`;
|
||||||
this.azureMonitorPath = `${routeNames.azureMonitor}/subscriptions`;
|
this.azureMonitorPath = `${routeNames.azureMonitor}/subscriptions`;
|
||||||
@ -148,78 +138,6 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Augment the results with links back to the azure console
|
|
||||||
*/
|
|
||||||
query(request: DataQueryRequest<AzureMonitorQuery>): Observable<DataQueryResponse> {
|
|
||||||
return super.query(request).pipe(
|
|
||||||
mergeMap((res: DataQueryResponse) => {
|
|
||||||
return from(this.processResponse(res));
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async processResponse(res: DataQueryResponse): Promise<DataQueryResponse> {
|
|
||||||
if (res.data) {
|
|
||||||
for (const df of res.data) {
|
|
||||||
const encodedQuery = df.meta?.custom?.encodedQuery;
|
|
||||||
if (encodedQuery && encodedQuery.length > 0) {
|
|
||||||
const url = await this.buildDeepLink(df.meta.custom);
|
|
||||||
if (url?.length) {
|
|
||||||
for (const field of df.fields) {
|
|
||||||
field.config.links = [
|
|
||||||
{
|
|
||||||
url: url,
|
|
||||||
title: 'View in Azure Portal',
|
|
||||||
targetBlank: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async buildDeepLink(customMeta: Record<string, any>) {
|
|
||||||
const base64Enc = encodeURIComponent(customMeta.encodedQuery);
|
|
||||||
const resource = encodeURIComponent(customMeta.resource);
|
|
||||||
|
|
||||||
const url =
|
|
||||||
`${this.azurePortalUrl}/#blade/Microsoft_OperationsManagementSuite_Workspace/` +
|
|
||||||
`AnalyticsBlade/initiator/AnalyticsShareLinkToQuery/isQueryEditorVisible/true/scope/` +
|
|
||||||
`%7B%22resources%22%3A%5B%7B%22resourceId%22%3A%22${resource}` +
|
|
||||||
`%22%7D%5D%7D/query/${base64Enc}/isQueryBase64Compressed/true/timespanInIsoFormat/P1D`;
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getWorkspaceDetails(workspaceId: string) {
|
|
||||||
if (!this.defaultSubscriptionId) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
const response = await this.getWorkspaceList(this.defaultSubscriptionId);
|
|
||||||
|
|
||||||
const details = response.value.find((o: any) => {
|
|
||||||
return o.properties.customerId === workspaceId;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!details) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const regex = /.*resourcegroups\/(.*)\/providers.*/;
|
|
||||||
const results = regex.exec(details.id);
|
|
||||||
if (!results || results.length < 2) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
workspace: details.name,
|
|
||||||
resourceGroup: results[1],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
In 7.5.x it used to be possible to set a default workspace id in the config on the auth page.
|
In 7.5.x it used to be possible to set a default workspace id in the config on the auth page.
|
||||||
This has been deprecated, however is still used by a few legacy template queries.
|
This has been deprecated, however is still used by a few legacy template queries.
|
||||||
|
Loading…
Reference in New Issue
Block a user