Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Application to datasource configuration #1107

Merged
merged 14 commits into from
Jan 23, 2025
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,4 @@ require (
google.golang.org/protobuf v1.35.2 // indirect
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
)
2 changes: 1 addition & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -329,4 +329,4 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
10 changes: 7 additions & 3 deletions pkg/azuredx/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
type AdxClient interface {
TestKustoRequest(ctx context.Context, datasourceSettings *models.DatasourceSettings, properties *models.Properties, additionalHeaders map[string]string) error
TestARGsRequest(ctx context.Context, datasourceSettings *models.DatasourceSettings, properties *models.Properties, additionalHeaders map[string]string) error
KustoRequest(ctx context.Context, cluster string, url string, payload models.RequestPayload, userTrackingEnabled bool) (*models.TableResponse, error)
KustoRequest(ctx context.Context, cluster string, url string, payload models.RequestPayload, userTrackingEnabled bool, application string) (*models.TableResponse, error)
ARGClusterRequest(ctx context.Context, payload models.ARGRequestPayload, additionalHeaders map[string]string) ([]models.ClusterOption, error)
}

Expand Down Expand Up @@ -196,7 +196,7 @@ func (c *Client) testManagementClient(ctx context.Context, _ *models.DatasourceS
// KustoRequest executes a Kusto Query language request to Azure's Data Explorer V1 REST API
// and returns a TableResponse. If there is a query syntax error, the error message inside
// the API's JSON error response is returned as well (if available).
func (c *Client) KustoRequest(ctx context.Context, clusterUrl string, path string, payload models.RequestPayload, userTrackingEnabled bool) (*models.TableResponse, error) {
func (c *Client) KustoRequest(ctx context.Context, clusterUrl string, path string, payload models.RequestPayload, userTrackingEnabled bool, application string) (*models.TableResponse, error) {
buf, err := json.Marshal(payload)
if err != nil {
return nil, errorsource.DownstreamError(fmt.Errorf("no Azure request serial: %w", err), false)
Expand All @@ -214,7 +214,11 @@ func (c *Client) KustoRequest(ctx context.Context, clusterUrl string, path strin

req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-ms-app", "Grafana-ADX")
if application == "" {
application = "Grafana-ADX"
}
req.Header.Set("x-ms-app", application)
// req.Header.Set("x-ms-app", "Grafana-ADX")
if payload.QuerySource == "" {
payload.QuerySource = "unspecified"
}
Expand Down
10 changes: 5 additions & 5 deletions pkg/azuredx/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func TestClient(t *testing.T) {
}

client := &Client{httpClientKusto: server.Client()}
table, err := client.KustoRequest(context.Background(), server.URL, "", payload, false)
table, err := client.KustoRequest(context.Background(), server.URL, "", payload, false, "Grafana-ADX")
require.NoError(t, err)
require.NotNil(t, table)
})
Expand All @@ -64,7 +64,7 @@ func TestClient(t *testing.T) {
}

client := &Client{httpClientKusto: server.Client()}
table, err := client.KustoRequest(context.Background(), server.URL, "", payload, false)
table, err := client.KustoRequest(context.Background(), server.URL, "", payload, false, "Grafana-ADX")
require.Nil(t, table)
require.NotNil(t, err)
require.Contains(t, err.Error(), "Request is invalid and cannot be processed: Syntax error: SYN0002: A recognition error occurred. [line:position=1:9]. Query: 'PerfTest take 5'")
Expand All @@ -74,7 +74,7 @@ func TestClient(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
require.Equal(t, "application/json", req.Header.Get("Accept"))
require.Equal(t, "application/json", req.Header.Get("Content-Type"))
require.Equal(t, "Grafana-ADX", req.Header.Get("x-ms-app"))
require.NotEmpty(t, req.Header.Get("x-ms-app"), "Header 'x-ms-app' should not be empty")
}))
defer server.Close()

Expand All @@ -85,7 +85,7 @@ func TestClient(t *testing.T) {
}

client := &Client{httpClientKusto: server.Client()}
table, err := client.KustoRequest(context.Background(), server.URL, "", payload, false)
table, err := client.KustoRequest(context.Background(), server.URL, "", payload, false, "Grafana-ADX")
require.Nil(t, table)
require.NotNil(t, err)
})
Expand Down Expand Up @@ -113,7 +113,7 @@ func TestClient(t *testing.T) {
Login: "test-user",
},
})
table, err := client.KustoRequest(ctxWithUser, server.URL, "", payload, true)
table, err := client.KustoRequest(ctxWithUser, server.URL, "", payload, true, "Grafana-ADX")
require.Nil(t, table)
require.NotNil(t, err)
})
Expand Down
4 changes: 2 additions & 2 deletions pkg/azuredx/datasource.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,13 @@ func (adx *AzureDataExplorer) modelQuery(ctx context.Context, q models.QueryMode
// errorsource set in SanitizeClusterUri
return backend.DataResponse{}, err
}

application := adx.settings.Application
tableRes, err := adx.client.KustoRequest(ctx, sanitized, "/v1/rest/query", models.RequestPayload{
CSL: q.Query,
DB: database,
Properties: props,
QuerySource: q.QuerySource,
}, adx.settings.EnableUserTracking)
}, adx.settings.EnableUserTracking, application)
if err != nil {
backend.Logger.Debug("error building kusto request", "error", err.Error())
// errorsource set in KustoRequest
Expand Down
13 changes: 7 additions & 6 deletions pkg/azuredx/datasource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

var (
kustoRequestMock func(url string, cluster string, payload models.RequestPayload, enableUserTracking bool) (*models.TableResponse, error)
kustoRequestMock func(url string, cluster string, payload models.RequestPayload, enableUserTracking bool, application string) (*models.TableResponse, error)
ARGClusterRequestMock func(payload models.ARGRequestPayload, additionalHeaders map[string]string) ([]models.ClusterOption, error)
table = &models.TableResponse{
Tables: []models.Table{
Expand All @@ -27,11 +27,12 @@ func TestDatasource(t *testing.T) {
var adx AzureDataExplorer
const UserLogin string = "user-login"
const ClusterURL string = "base-url"
const Application string = "Grafana-ADX"

t.Run("When running a query the right args should be passed to KustoRequest", func(t *testing.T) {
adx = AzureDataExplorer{}
adx.client = &fakeClient{}
adx.settings = &models.DatasourceSettings{EnableUserTracking: true, ClusterURL: ClusterURL}
adx.settings = &models.DatasourceSettings{EnableUserTracking: true, ClusterURL: ClusterURL, Application: Application}
query := backend.DataQuery{
RefID: "",
QueryType: "",
Expand All @@ -40,7 +41,7 @@ func TestDatasource(t *testing.T) {
TimeRange: backend.TimeRange{},
JSON: []byte(`{"resultFormat": "table","querySource": "schema","database":"test-database"}`),
}
kustoRequestMock = func(url string, cluster string, payload models.RequestPayload, enableUserTracking bool) (*models.TableResponse, error) {
kustoRequestMock = func(url string, cluster string, payload models.RequestPayload, enableUserTracking bool, application string) (*models.TableResponse, error) {
require.Equal(t, "/v1/rest/query", url)
require.Equal(t, ClusterURL, cluster)
require.Equal(t, payload.DB, "test-database")
Expand All @@ -62,7 +63,7 @@ func TestDatasource(t *testing.T) {
TimeRange: backend.TimeRange{},
JSON: []byte(`{"resultFormat": "table","querySource": "schema"}`),
}
kustoRequestMock = func(_ string, _ string, payload models.RequestPayload, _ bool) (*models.TableResponse, error) {
kustoRequestMock = func(_ string, _ string, payload models.RequestPayload, _ bool, _ string) (*models.TableResponse, error) {
require.Equal(t, payload.DB, "test-default-database")
return table, nil
}
Expand Down Expand Up @@ -100,8 +101,8 @@ func (c *fakeClient) TestARGsRequest(_ context.Context, _ *models.DatasourceSett
panic("not implemented")
}

func (c *fakeClient) KustoRequest(_ context.Context, cluster string, url string, payload models.RequestPayload, enableUserTracking bool) (*models.TableResponse, error) {
return kustoRequestMock(url, cluster, payload, enableUserTracking)
func (c *fakeClient) KustoRequest(_ context.Context, cluster string, url string, payload models.RequestPayload, enableUserTracking bool, application string) (*models.TableResponse, error) {
return kustoRequestMock(url, cluster, payload, enableUserTracking, application)
}

func (c *fakeClient) ARGClusterRequest(_ context.Context, payload models.ARGRequestPayload, additionalHeaders map[string]string) ([]models.ClusterOption, error) {
Expand Down
2 changes: 2 additions & 0 deletions pkg/azuredx/models/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ type DatasourceSettings struct {
CacheMaxAge string `json:"cacheMaxAge"`
DynamicCaching bool `json:"dynamicCaching"`
EnableUserTracking bool `json:"enableUserTracking"`
Application string `json:"application"`


// QueryTimeoutRaw is a duration string set in the datasource settings and corresponds
// to the server execution timeout.
Expand Down
8 changes: 4 additions & 4 deletions pkg/azuredx/resource_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ func (adx *AzureDataExplorer) getSchema(rw http.ResponseWriter, req *http.Reques
respondWithError(rw, http.StatusBadRequest, "Invalid clusterUri", err)
return
}

application := adx.settings.Application
// Default to not sending the user request headers for schema requests
response, err := adx.client.KustoRequest(req.Context(), sanitized, ManagementApiPath, payload, false)
response, err := adx.client.KustoRequest(req.Context(), sanitized, ManagementApiPath, payload, false, application)
if err != nil {
respondWithError(rw, http.StatusInternalServerError, "Azure query unsuccessful", err)
return
Expand Down Expand Up @@ -196,9 +196,9 @@ func (adx *AzureDataExplorer) getDatabases(rw http.ResponseWriter, req *http.Req
respondWithError(rw, http.StatusBadRequest, "Invalid clusterUri", err)
return
}

application := adx.settings.Application
// Default to not sending the user request headers for schema requests
response, err := adx.client.KustoRequest(req.Context(), sanitized, ManagementApiPath, payload, false)
response, err := adx.client.KustoRequest(req.Context(), sanitized, ManagementApiPath, payload, false, application)
if err != nil {
respondWithError(rw, http.StatusInternalServerError, "Azure query unsuccessful", err)
return
Expand Down
4 changes: 2 additions & 2 deletions pkg/azuredx/resource_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func (c *failingClient) TestARGsRequest(_ context.Context, _ *models.DatasourceS
panic("not implemented")
}

func (c *failingClient) KustoRequest(_ context.Context, _ string, _ string, _ models.RequestPayload, _ bool) (*models.TableResponse, error) {
func (c *failingClient) KustoRequest(_ context.Context, _ string, _ string, _ models.RequestPayload, _ bool, _ string) (*models.TableResponse, error) {
return nil, fmt.Errorf("HTTP error: %v - %v", http.StatusBadRequest, "")
}

Expand All @@ -100,7 +100,7 @@ func (c *workingClient) TestARGsRequest(_ context.Context, _ *models.DatasourceS
panic("not implemented")
}

func (c *workingClient) KustoRequest(_ context.Context, _ string, _ string, _ models.RequestPayload, _ bool) (*models.TableResponse, error) {
func (c *workingClient) KustoRequest(_ context.Context, _ string, _ string, _ models.RequestPayload, _ bool, _ string) (*models.TableResponse, error) {
return &models.TableResponse{
Tables: []models.Table{
{
Expand Down
30 changes: 30 additions & 0 deletions src/components/ConfigEditor/ApplicationConfig.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import { DataSourcePluginOptionsEditorProps } from '@grafana/data';
import { Field, Input } from '@grafana/ui';
import { AdxDataSourceOptions, AdxDataSourceSecureOptions } from 'types';
import { selectors } from 'test/selectors';

interface ApplicationConfigProps
extends DataSourcePluginOptionsEditorProps<AdxDataSourceOptions, AdxDataSourceSecureOptions> {
updateJsonData: <T extends keyof AdxDataSourceOptions>(fieldName: T, value: AdxDataSourceOptions[T]) => void;
}

const ApplicationConfig: React.FC<ApplicationConfigProps> = ({ options, updateJsonData }) => {
const { jsonData } = options;

return (
<Field label="Application name (Optional)" description="Application name to be displayed in ADX.">
<Input
aria-label="Application"
data-testid={selectors.components.applicationEditor.application.input}
value={jsonData.application}
id="adx-application"
placeholder="Grafana-ADX"
width={60}
onChange={(ev: React.ChangeEvent<HTMLInputElement>) => updateJsonData('application', ev.target.value)}
/>
</Field>
);
};

export default ApplicationConfig;
3 changes: 3 additions & 0 deletions src/components/ConfigEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ConnectionConfig from './ConnectionConfig';
import DatabaseConfig from './DatabaseConfig';
import QueryConfig from './QueryConfig';
import TrackingConfig from './TrackingConfig';
import ApplicationConfig from './ApplicationConfig';
import {
getCredentials,
getDefaultCredentials,
Expand Down Expand Up @@ -52,6 +53,7 @@ const ConfigEditor: React.FC<ConfigEditorProps> = (props) => {
options.jsonData.cacheMaxAge ||
options.jsonData.useSchemaMapping ||
options.jsonData.enableUserTracking ||
options.jsonData.application ||
options.secureJsonFields['OpenAIAPIKey']
),
[options]
Expand Down Expand Up @@ -118,6 +120,7 @@ const ConfigEditor: React.FC<ConfigEditorProps> = (props) => {
>
<QueryConfig options={options} onOptionsChange={onOptionsChange} updateJsonData={updateJsonData} />
<DatabaseConfig options={options} onOptionsChange={onOptionsChange} updateJsonData={updateJsonData} />
<ApplicationConfig options={options} onOptionsChange={onOptionsChange} updateJsonData={updateJsonData} />
<TrackingConfig options={options} onOptionsChange={onOptionsChange} updateJsonData={updateJsonData} />
</ConfigSection>

Expand Down
1 change: 1 addition & 0 deletions src/components/__fixtures__/ConfigEditor.fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const mockConfigEditorProps = (optionsOverrides?: Partial<ConfigEditorPro
useSchemaMapping: false,
enableUserTracking: true,
clusterUrl: Chance().url(),
application: Chance().word(),
},
readOnly: true,
withCredentials: true,
Expand Down
1 change: 1 addition & 0 deletions src/components/__fixtures__/Datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const mockDatasourceOptions: DataSourcePluginOptionsEditorProps<
useSchemaMapping: false,
enableUserTracking: false,
clusterUrl: 'clusterUrl',
application: ''
},
secureJsonFields: {},
readOnly: false,
Expand Down
9 changes: 7 additions & 2 deletions src/datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,14 @@
private expressionParser: KustoExpressionParser;
private defaultEditorMode: EditorMode;
private schemaMapper: AdxSchemaMapper;
private application: string;

constructor(private instanceSettings: DataSourceInstanceSettings<AdxDataSourceOptions>) {
super(instanceSettings);

const useSchemaMapping = instanceSettings.jsonData.useSchemaMapping ?? false;
const schemaMapping = instanceSettings.jsonData.schemaMappings ?? [];

const application = instanceSettings.jsonData.application ?? 'Grafana-ADX';
this.backendSrv = getBackendSrv();
this.templateSrv = getTemplateSrv();
this.defaultOrFirstDatabase = instanceSettings.jsonData.defaultDatabase;
Expand All @@ -63,6 +64,7 @@
this.defaultEditorMode = instanceSettings.jsonData.defaultEditorMode ?? EditorMode.Visual;
this.schemaMapper = new AdxSchemaMapper(useSchemaMapping, schemaMapping);
this.expressionParser = new KustoExpressionParser(this.templateSrv);
this.application = application;
this.parseExpression = this.parseExpression.bind(this);
this.autoCompleteQuery = this.autoCompleteQuery.bind(this);
this.getSchemaMapper = this.getSchemaMapper.bind(this);
Expand Down Expand Up @@ -313,6 +315,9 @@
getDefaultEditorMode(): EditorMode {
return this.defaultEditorMode;
}
getApplication(): string {
return this.application
}

async autoCompleteQuery(query: AutoCompleteQuery, columns: AdxColumnSchema[] | undefined): Promise<string[]> {
const autoQuery = this.expressionParser.toAutoCompleteQuery(query, columns);
Expand All @@ -329,7 +334,7 @@
query: autoQuery,
resultFormat: 'table',
querySource: 'autocomplete',
clusterUri: query.clusterUri,
clusterUri: query.clusterUri
};

const response = await lastValueFrom(
Expand Down Expand Up @@ -414,8 +419,8 @@
for (const frame of frames) {
for (let index = 0; index < frame.length; index++) {
result.push({
Name: frame.fields[nameIndex].values.get(index),

Check warning on line 422 in src/datasource.ts

View workflow job for this annotation

GitHub Actions / build

'get' is deprecated. Use [idx]. This only exists to help migrate Vector to Array

Check warning on line 422 in src/datasource.ts

View workflow job for this annotation

GitHub Actions / build

'get' is deprecated. Use [idx]. This only exists to help migrate Vector to Array
CslType: frame.fields[typeIndex].values.get(index),

Check warning on line 423 in src/datasource.ts

View workflow job for this annotation

GitHub Actions / build

'get' is deprecated. Use [idx]. This only exists to help migrate Vector to Array

Check warning on line 423 in src/datasource.ts

View workflow job for this annotation

GitHub Actions / build

'get' is deprecated. Use [idx]. This only exists to help migrate Vector to Array
});
}
}
Expand All @@ -428,7 +433,7 @@

for (const frame of frames) {
for (const field of frame.fields) {
const json = JSON.parse(field.values.get(0));

Check warning on line 436 in src/datasource.ts

View workflow job for this annotation

GitHub Actions / build

'get' is deprecated. Use [idx]. This only exists to help migrate Vector to Array

Check warning on line 436 in src/datasource.ts

View workflow job for this annotation

GitHub Actions / build

'get' is deprecated. Use [idx]. This only exists to help migrate Vector to Array

if (json === null) {
console.log('error with field', field);
Expand Down
5 changes: 5 additions & 0 deletions src/test/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { E2ESelectors } from '@grafana/e2e-selectors';

export const components = {
applicationEditor: {
application : {
input: 'data-testid application'
}
},
configEditor: {
authType: {
input: 'data-testid azure-auth',
Expand Down
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export interface AdxDataSourceOptions extends AzureDataSourceJsonData {
schemaMappings?: Array<Partial<SchemaMapping>>;
enableUserTracking: boolean;
clusterUrl: string;
application: string;
enableSecureSocksProxy?: boolean;
// legacy options
azureCloud?: string;
Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12663,4 +12663,4 @@ yocto-queue@^0.1.0:
yocto-queue@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251"
integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
Loading