Merge remote-tracking branch 'origin/main' into grafana-git-ui-sync
This commit is contained in:
commit
7e54e49b5a
13 files changed
+310
-169
No files matched your search
@@ -28,3 +28,83 @@ try to cherry-pick the PR merge commit into that branch and open a PR. You must
|
||||
If there are merge conflicts the bot will write a comment on the source PR saying the cherry-pick failed. In this case you have to do the cherry pick and backport PR manually.
|
||||
|
||||
The backport logic is written [here](https://github.com/grafana/grafana-github-actions/blob/main/backport/backport.ts)
|
||||
|
||||
## Auto triager bot
|
||||
|
||||
The auto triager bot is a github action that **assigns** labels to issues based on the issue contents. The logic to assign
|
||||
labels is its own program and lives [here](https://github.com/grafana/auto-triager). It uses an LLM to do this.
|
||||
|
||||
The bot runs **every time** a new issue is opened in the grafana/grafana repository. You can find the bot definition [here](https://github.com/grafana/grafana/blob/main/.github/workflows/issue-opened.yml#L61)
|
||||
|
||||
The job only assign labels when the issue author is not a member of the Grafana organization in **GitHub**. The bot concurrency is 1.
|
||||
|
||||
This bot is not responsible for assigning teams, the [commands](https://github.com/grafana/grafana/blob/main/.github/workflows/commands.yml) workflow is responsible for doing that
|
||||
|
||||
### General diagram
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
actor User
|
||||
participant GH as GitHub
|
||||
participant AT as Auto Triager Job
|
||||
participant ATP as Auto Triager Program
|
||||
participant LLM as LLM Service
|
||||
participant CJ as Commands Job
|
||||
|
||||
User->>GH: Opens Issue
|
||||
GH->>GH: Check if user in Grafana org
|
||||
alt User not in Grafana org
|
||||
GH->>AT: Trigger Auto Triager Job
|
||||
AT->>ATP: Execute program
|
||||
ATP->>LLM: Send issue content & categories
|
||||
LLM-->>ATP: Return matching categories
|
||||
ATP-->>GH: Assign labels to issue
|
||||
GH->>CJ: Trigger Commands Job
|
||||
CJ->>CJ: Read commands.json
|
||||
CJ-->>GH: Assign teams based on labels
|
||||
end
|
||||
```
|
||||
|
||||
### Team definitions
|
||||
|
||||
The team associated with labels are defined [here](https://github.com/grafana/grafana/blob/main/.github/commands.json).
|
||||
This bot is not responsible for assigning teams, the [commands](https://github.com/grafana/grafana/blob/main/.github/workflows/commands.yml) workflow is responsible for doing that.
|
||||
|
||||
The commands workflow code can be found [here](https://github.com/grafana/grafana-github-actions/tree/main/commands)
|
||||
|
||||
### Categories/Labels definitions
|
||||
|
||||
The categories (or labels) and the types used to categorize issues are defined in this same repository [here](https://github.com/grafana/grafana/tree/main/.github/workflows/auto-triager) the [prompt](https://github.com/grafana/grafana/blob/main/.github/workflows/auto-triager/prompt.txt) that is passed to the LLM is also defined there.
|
||||
|
||||
If you are adding a new category in the auto-triager repository you must define a team that owns the label in the
|
||||
[commands.json](https://github.com/grafana/grafana/blob/main/.github/commands.json).
|
||||
|
||||
If you remove a label from the [commands.json](https://github.com/grafana/grafana/blob/main/.github/commands.json) and it doesn't have any other
|
||||
team associated with it you must remove it from the [labels file](https://github.com/grafana/grafana/blob/main/.github/workflows/auto-triager/labels.txt)
|
||||
|
||||
### Secrets
|
||||
|
||||
The bot secrets live in the vault. It uses a [shared workflow](https://github.com/grafana/shared-workflows/tree/main/actions/get-vault-secrets) to get the vault secrets, the
|
||||
workflow requires a token with `contents:read` and `id-token:write` scopes for it to work.
|
||||
|
||||
### How to detect the bot is working?
|
||||
|
||||
The list of [unlabeled issues](https://github.com/grafana/grafana/issues?q=is%3Aopen+is%3Aissue+no%3Alabel) should remain empty as long as the bot is running.
|
||||
There might be issues in the list if some team member removed all labels, but if the list grows to more
|
||||
than 5 it is likely the bot is not working correctly.
|
||||
|
||||
### What to do if this bot is not working?
|
||||
|
||||
You can contact the plugins platform team in slack `#grafana-plugins-platform` and inform about the issue.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
Possible reasons why the bot is not working:
|
||||
|
||||
* The OpenAI API key is not valid anymore. The action output will show this in its error log. A new key needs to be
|
||||
generated via the OpenAI UI and its value updated in vault. See [the action](https://github.com/grafana/grafana/blob/main/.github/workflows/issue-opened.yml#L72) to find the correct path to
|
||||
update the key.
|
||||
* The Slack webhook URL is not valid anymore. The action output will show this in its error log or the
|
||||
#triage-automation-ci channel will stop showing messages about issue triaging. A new slack webhook url needs to be
|
||||
generated for the auto triager app and its value updated in vault.
|
||||
* This bot is not responsible for assigning teams, the [commands](https://github.com/grafana/grafana/blob/main/.github/workflows/commands.yml) workflow is responsible for doing that
|
||||
@@ -92,3 +92,7 @@ Query results include lines that match any of the rules.
|
||||
Only users with data source `Admin` permissions can edit LBAC for data sources rules in the **Data source permissions** tab because changing LBAC rules requires the same access level as editing data source permissions.
|
||||
|
||||
To set up LBAC for data sources for a data source, refer to [Configure LBAC for data sources](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/administration/data-source-management/teamlbac/configure-teamlbac-for-loki/).
|
||||
|
||||
## Provisioning of LBAC rules
|
||||
|
||||
We recommend using our Terraform provider to set up provisioning for [Resource data source config LBAC rules] (https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/data_source_config_lbac_rules). Refer to our provider documentation to learn how to configure rules for a data source.
|
||||
+2
-2
@@ -272,8 +272,8 @@
|
||||
"@grafana/prometheus": "workspace:*",
|
||||
"@grafana/runtime": "workspace:*",
|
||||
"@grafana/saga-icons": "workspace:*",
|
||||
"@grafana/scenes": "^5.35.0",
|
||||
"@grafana/scenes-react": "^5.35.0",
|
||||
"@grafana/scenes": "5.36.3",
|
||||
"@grafana/scenes-react": "5.36.3",
|
||||
"@grafana/schema": "workspace:*",
|
||||
"@grafana/sql": "workspace:*",
|
||||
"@grafana/ui": "workspace:*",
|
||||
|
||||
@@ -625,6 +625,9 @@ func getCorrelationWorkspaces(ctx context.Context, baseResource string, resource
|
||||
}()
|
||||
|
||||
if res.StatusCode/100 != 2 {
|
||||
if res.StatusCode == 404 {
|
||||
return AzureCorrelationAPIResponse{}, backend.DownstreamError(fmt.Errorf("requested trace not found by Application Insights indexing. Select the relevant Application Insights resource to search for the Operation ID directly"))
|
||||
}
|
||||
return AzureCorrelationAPIResponse{}, utils.CreateResponseErrorFromStatusCode(res.StatusCode, res.Status, body)
|
||||
}
|
||||
var data AzureCorrelationAPIResponse
|
||||
|
||||
@@ -26,6 +26,10 @@ func TestBuildAppInsightsQuery(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if strings.Contains(r.URL.Path, "missing-op-id") {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
var correlationRes AzureCorrelationAPIResponse
|
||||
if strings.Contains(r.URL.Path, "test-op-id") {
|
||||
@@ -95,7 +99,7 @@ func TestBuildAppInsightsQuery(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
queryModel backend.DataQuery
|
||||
azureLogAnalyticsQuery AzureLogAnalyticsQuery
|
||||
azureLogAnalyticsQuery *AzureLogAnalyticsQuery
|
||||
Err require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
@@ -114,7 +118,7 @@ func TestBuildAppInsightsQuery(t *testing.T) {
|
||||
TimeRange: timeRange,
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureTraces),
|
||||
},
|
||||
azureLogAnalyticsQuery: AzureLogAnalyticsQuery{
|
||||
azureLogAnalyticsQuery: &AzureLogAnalyticsQuery{
|
||||
RefID: "A",
|
||||
ResultFormat: dataquery.ResultFormatTable,
|
||||
URL: "v1/apps/r1/query",
|
||||
@@ -190,7 +194,7 @@ func TestBuildAppInsightsQuery(t *testing.T) {
|
||||
TimeRange: timeRange,
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureTraces),
|
||||
},
|
||||
azureLogAnalyticsQuery: AzureLogAnalyticsQuery{
|
||||
azureLogAnalyticsQuery: &AzureLogAnalyticsQuery{
|
||||
RefID: "A",
|
||||
ResultFormat: dataquery.ResultFormatTable,
|
||||
URL: "v1/apps/r1/query",
|
||||
@@ -264,7 +268,7 @@ func TestBuildAppInsightsQuery(t *testing.T) {
|
||||
TimeRange: timeRange,
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureTraces),
|
||||
},
|
||||
azureLogAnalyticsQuery: AzureLogAnalyticsQuery{
|
||||
azureLogAnalyticsQuery: &AzureLogAnalyticsQuery{
|
||||
RefID: "A",
|
||||
ResultFormat: dataquery.ResultFormatTable,
|
||||
URL: "v1/apps/r1/query",
|
||||
@@ -337,7 +341,7 @@ func TestBuildAppInsightsQuery(t *testing.T) {
|
||||
TimeRange: timeRange,
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureTraces),
|
||||
},
|
||||
azureLogAnalyticsQuery: AzureLogAnalyticsQuery{
|
||||
azureLogAnalyticsQuery: &AzureLogAnalyticsQuery{
|
||||
RefID: "A",
|
||||
ResultFormat: dataquery.ResultFormatTable,
|
||||
URL: "v1/apps/r1/query",
|
||||
@@ -413,7 +417,7 @@ func TestBuildAppInsightsQuery(t *testing.T) {
|
||||
TimeRange: timeRange,
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureTraces),
|
||||
},
|
||||
azureLogAnalyticsQuery: AzureLogAnalyticsQuery{
|
||||
azureLogAnalyticsQuery: &AzureLogAnalyticsQuery{
|
||||
RefID: "A",
|
||||
ResultFormat: dataquery.ResultFormatTable,
|
||||
URL: "v1/apps/r1/query",
|
||||
@@ -493,7 +497,7 @@ func TestBuildAppInsightsQuery(t *testing.T) {
|
||||
TimeRange: timeRange,
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureTraces),
|
||||
},
|
||||
azureLogAnalyticsQuery: AzureLogAnalyticsQuery{
|
||||
azureLogAnalyticsQuery: &AzureLogAnalyticsQuery{
|
||||
RefID: "A",
|
||||
ResultFormat: dataquery.ResultFormatTable,
|
||||
URL: "v1/apps/r1/query",
|
||||
@@ -573,7 +577,7 @@ func TestBuildAppInsightsQuery(t *testing.T) {
|
||||
TimeRange: timeRange,
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureTraces),
|
||||
},
|
||||
azureLogAnalyticsQuery: AzureLogAnalyticsQuery{
|
||||
azureLogAnalyticsQuery: &AzureLogAnalyticsQuery{
|
||||
RefID: "A",
|
||||
ResultFormat: dataquery.ResultFormatTable,
|
||||
URL: "v1/apps/r1/query",
|
||||
@@ -651,7 +655,7 @@ func TestBuildAppInsightsQuery(t *testing.T) {
|
||||
TimeRange: timeRange,
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureTraces),
|
||||
},
|
||||
azureLogAnalyticsQuery: AzureLogAnalyticsQuery{
|
||||
azureLogAnalyticsQuery: &AzureLogAnalyticsQuery{
|
||||
RefID: "A",
|
||||
ResultFormat: dataquery.ResultFormatTrace,
|
||||
URL: "v1/apps/r1/query",
|
||||
@@ -724,7 +728,7 @@ func TestBuildAppInsightsQuery(t *testing.T) {
|
||||
TimeRange: timeRange,
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureTraces),
|
||||
},
|
||||
azureLogAnalyticsQuery: AzureLogAnalyticsQuery{
|
||||
azureLogAnalyticsQuery: &AzureLogAnalyticsQuery{
|
||||
RefID: "A",
|
||||
ResultFormat: dataquery.ResultFormatTrace,
|
||||
URL: "v1/apps/r1/query",
|
||||
@@ -800,7 +804,7 @@ func TestBuildAppInsightsQuery(t *testing.T) {
|
||||
TimeRange: timeRange,
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureTraces),
|
||||
},
|
||||
azureLogAnalyticsQuery: AzureLogAnalyticsQuery{
|
||||
azureLogAnalyticsQuery: &AzureLogAnalyticsQuery{
|
||||
RefID: "A",
|
||||
ResultFormat: dataquery.ResultFormatTrace,
|
||||
URL: "v1/apps/r1/query",
|
||||
@@ -842,7 +846,7 @@ func TestBuildAppInsightsQuery(t *testing.T) {
|
||||
TimeRange: timeRange,
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureTraces),
|
||||
},
|
||||
azureLogAnalyticsQuery: AzureLogAnalyticsQuery{
|
||||
azureLogAnalyticsQuery: &AzureLogAnalyticsQuery{
|
||||
RefID: "A",
|
||||
ResultFormat: dataquery.ResultFormatTrace,
|
||||
URL: "v1/apps/r1/query",
|
||||
@@ -920,7 +924,7 @@ func TestBuildAppInsightsQuery(t *testing.T) {
|
||||
TimeRange: timeRange,
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureTraces),
|
||||
},
|
||||
azureLogAnalyticsQuery: AzureLogAnalyticsQuery{
|
||||
azureLogAnalyticsQuery: &AzureLogAnalyticsQuery{
|
||||
RefID: "A",
|
||||
ResultFormat: dataquery.ResultFormatTrace,
|
||||
URL: "v1/apps/r1/query",
|
||||
@@ -997,7 +1001,7 @@ func TestBuildAppInsightsQuery(t *testing.T) {
|
||||
TimeRange: timeRange,
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureTraces),
|
||||
},
|
||||
azureLogAnalyticsQuery: AzureLogAnalyticsQuery{
|
||||
azureLogAnalyticsQuery: &AzureLogAnalyticsQuery{
|
||||
RefID: "A",
|
||||
ResultFormat: dataquery.ResultFormatTrace,
|
||||
URL: "v1/apps/r1/query",
|
||||
@@ -1076,7 +1080,7 @@ func TestBuildAppInsightsQuery(t *testing.T) {
|
||||
TimeRange: timeRange,
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureTraces),
|
||||
},
|
||||
azureLogAnalyticsQuery: AzureLogAnalyticsQuery{
|
||||
azureLogAnalyticsQuery: &AzureLogAnalyticsQuery{
|
||||
RefID: "A",
|
||||
ResultFormat: dataquery.ResultFormatTrace,
|
||||
URL: "v1/apps/r1/query",
|
||||
@@ -1147,13 +1151,34 @@ func TestBuildAppInsightsQuery(t *testing.T) {
|
||||
},
|
||||
Err: require.NoError,
|
||||
},
|
||||
{
|
||||
name: "trace query with missing operation ID",
|
||||
queryModel: backend.DataQuery{
|
||||
JSON: []byte(fmt.Sprintf(`{
|
||||
"queryType": "Azure Traces",
|
||||
"azureTraces": {
|
||||
"resources": ["/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.Insights/components/r1"],
|
||||
"resultFormat": "%s",
|
||||
"traceTypes": ["trace"],
|
||||
"operationId": "missing-op-id"
|
||||
}
|
||||
}`, dataquery.ResultFormatTable)),
|
||||
RefID: "A",
|
||||
TimeRange: timeRange,
|
||||
QueryType: string(dataquery.AzureQueryTypeAzureTraces),
|
||||
},
|
||||
azureLogAnalyticsQuery: nil,
|
||||
Err: func(tt require.TestingT, err error, i ...interface{}) {
|
||||
require.ErrorContains(tt, err, "requested trace not found by Application Insights indexing. Select the relevant Application Insights resource to search for the Operation ID directly")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
query, err := buildAppInsightsQuery(ctx, tt.queryModel, dsInfo, appInsightsRegExp, log.NewNullLogger())
|
||||
tt.Err(t, err)
|
||||
if diff := cmp.Diff(&tt.azureLogAnalyticsQuery, query); diff != "" {
|
||||
if diff := cmp.Diff(tt.azureLogAnalyticsQuery, query); diff != "" {
|
||||
t.Errorf("Result mismatch (-want +got): \n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -7,7 +7,7 @@ import { SceneGridRow, VizPanel, sceneGraph } from '@grafana/scenes';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { Trans } from 'app/core/internationalization';
|
||||
|
||||
import { activateSceneObjectAndParentTree } from '../utils/utils';
|
||||
import { forceActivateFullSceneObjectTree } from '../utils/utils';
|
||||
|
||||
import { DashboardScene } from './DashboardScene';
|
||||
import { DashboardGridItem } from './layout-default/DashboardGridItem';
|
||||
@@ -65,7 +65,13 @@ export function PanelSearchLayout({ dashboard, panelSearch = '', panelsPerRow }:
|
||||
}
|
||||
|
||||
function PanelSearchHit({ panel }: { panel: VizPanel }) {
|
||||
useEffect(() => activateSceneObjectAndParentTree(panel), [panel]);
|
||||
useEffect(() => {
|
||||
const deactivate = forceActivateFullSceneObjectTree(panel);
|
||||
|
||||
return () => {
|
||||
deactivate?.();
|
||||
};
|
||||
}, [panel]);
|
||||
|
||||
return <panel.Component model={panel} />;
|
||||
}
|
||||
|
||||
@@ -275,6 +275,34 @@ export function activateSceneObjectAndParentTree(so: SceneObject): CancelActivat
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adaptation of activateSceneObjectAndParentTree specific for PanelSearchLayout use case with
|
||||
* with panelSearch and panelsPerRow custom panel filtering logic.
|
||||
*
|
||||
* Activating the whole tree because dashboard does not react to variable updates such as panel repeats
|
||||
*/
|
||||
export function forceActivateFullSceneObjectTree(so: SceneObject): CancelActivationHandler | undefined {
|
||||
let cancel: CancelActivationHandler | undefined;
|
||||
let parentCancel: CancelActivationHandler | undefined;
|
||||
|
||||
if (so.parent) {
|
||||
parentCancel = forceActivateFullSceneObjectTree(so.parent);
|
||||
}
|
||||
|
||||
if (!so.isActive) {
|
||||
cancel = so.activate();
|
||||
return () => {
|
||||
parentCancel?.();
|
||||
cancel?.();
|
||||
};
|
||||
}
|
||||
|
||||
return () => {
|
||||
parentCancel?.();
|
||||
cancel?.();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use activateSceneObjectAndParentTree instead.
|
||||
* Activates any inactive ancestors of the scene object.
|
||||
|
||||
+32
-1
@@ -8,8 +8,9 @@ import createMockDatasource from '../../__mocks__/datasource';
|
||||
import { invalidNamespaceError } from '../../__mocks__/errors';
|
||||
import createMockQuery from '../../__mocks__/query';
|
||||
import { selectors } from '../../e2e/selectors';
|
||||
import { AzureQueryType } from '../../types';
|
||||
import { AzureQueryType, ResultFormat } from '../../types';
|
||||
import { selectOptionInTest } from '../../utils/testUtils';
|
||||
import { createMockResourcePickerData } from '../MetricsQueryEditor/MetricsQueryEditor.test';
|
||||
|
||||
import QueryEditor from './QueryEditor';
|
||||
|
||||
@@ -204,4 +205,34 @@ describe('Azure Monitor QueryEditor', () => {
|
||||
expect(screen.getByTestId(selectors.components.queryEditor.userAuthFallbackAlert)).toBeInTheDocument()
|
||||
);
|
||||
});
|
||||
|
||||
it('should display the default subscription for exemplar type queries', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
const defaultSubscriptionId = 'default-subscription-id';
|
||||
mockDatasource.azureLogAnalyticsDatasource.getDefaultOrFirstSubscription = jest
|
||||
.fn()
|
||||
.mockResolvedValue(defaultSubscriptionId);
|
||||
const query = createMockQuery();
|
||||
delete query?.subscription;
|
||||
delete query?.azureTraces;
|
||||
query.queryType = AzureQueryType.TraceExemplar;
|
||||
query.query = 'test-operation-id';
|
||||
const onChange = jest.fn();
|
||||
|
||||
render(<QueryEditor query={query} datasource={mockDatasource} onChange={onChange} onRunQuery={() => {}} />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
azureTraces: {
|
||||
operationId: query.query,
|
||||
resultFormat: ResultFormat.Trace,
|
||||
resources: [`/subscriptions/${defaultSubscriptionId}`],
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
await waitFor(() => expect(screen.getByText(defaultSubscriptionId)).toBeInTheDocument());
|
||||
expect(await screen.getByDisplayValue('test-operation-id')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { debounce } from 'lodash';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useEffectOnce } from 'react-use';
|
||||
|
||||
import { CoreApp, QueryEditorProps } from '@grafana/data';
|
||||
import { config, reportInteraction } from '@grafana/runtime';
|
||||
@@ -43,6 +44,7 @@ const QueryEditor = ({
|
||||
const [errorMessage, setError] = useLastError();
|
||||
const onRunQuery = useMemo(() => debounce(baseOnRunQuery, 500), [baseOnRunQuery]);
|
||||
const [azureLogsCheatSheetModalOpen, setAzureLogsCheatSheetModalOpen] = useState(false);
|
||||
const [defaultSubscriptionId, setDefaultSubscriptionId] = useState('');
|
||||
|
||||
const onQueryChange = useCallback(
|
||||
(newQuery: AzureMonitorQuery) => {
|
||||
@@ -52,7 +54,15 @@ const QueryEditor = ({
|
||||
[onChange, onRunQuery]
|
||||
);
|
||||
|
||||
const query = usePreparedQuery(baseQuery, onQueryChange);
|
||||
useEffectOnce(() => {
|
||||
if (baseQuery.queryType === AzureQueryType.TraceExemplar) {
|
||||
datasource.azureLogAnalyticsDatasource.getDefaultOrFirstSubscription().then((subscription) => {
|
||||
setDefaultSubscriptionId(subscription || '');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const query = usePreparedQuery(baseQuery, onQueryChange, defaultSubscriptionId);
|
||||
|
||||
const subscriptionId = query.subscription || datasource.azureMonitorDatasource.defaultSubscriptionId;
|
||||
const basicLogsEnabled =
|
||||
|
||||
+10
-5
@@ -9,21 +9,22 @@ const DEFAULT_QUERY = {
|
||||
queryType: AzureQueryType.AzureMonitor,
|
||||
};
|
||||
|
||||
const transformExemplarQuery = (query: AzureMonitorQuery) => {
|
||||
const transformExemplarQuery = (query: AzureMonitorQuery, defaultSubscriptionId: string) => {
|
||||
if (query.queryType === AzureQueryType.TraceExemplar && query.query !== '' && !query.azureTraces) {
|
||||
query.azureTraces = {
|
||||
operationId: query.query,
|
||||
resultFormat: ResultFormat.Trace,
|
||||
resources: [`/subscriptions/${defaultSubscriptionId}`],
|
||||
};
|
||||
}
|
||||
|
||||
return query;
|
||||
};
|
||||
|
||||
const prepareQuery = (query: AzureMonitorQuery) => {
|
||||
const prepareQuery = (query: AzureMonitorQuery, defaultSubscriptionId: string) => {
|
||||
// Note: _.defaults does not apply default values deeply.
|
||||
const withDefaults = defaults({}, query, DEFAULT_QUERY);
|
||||
const transformedQuery = transformExemplarQuery(withDefaults);
|
||||
const transformedQuery = transformExemplarQuery(withDefaults, defaultSubscriptionId);
|
||||
const migratedQuery = migrateQuery(transformedQuery);
|
||||
|
||||
// If we didn't make any changes to the object, then return the original object to keep the
|
||||
@@ -34,8 +35,12 @@ const prepareQuery = (query: AzureMonitorQuery) => {
|
||||
/**
|
||||
* Returns queries with some defaults + migrations, and calls onChange function to notify if it changes
|
||||
*/
|
||||
const usePreparedQuery = (query: AzureMonitorQuery, onChangeQuery: (newQuery: AzureMonitorQuery) => void) => {
|
||||
const preparedQuery = useMemo(() => prepareQuery(query), [query]);
|
||||
const usePreparedQuery = (
|
||||
query: AzureMonitorQuery,
|
||||
onChangeQuery: (newQuery: AzureMonitorQuery) => void,
|
||||
defaultSubscriptionId: string
|
||||
) => {
|
||||
const preparedQuery = useMemo(() => prepareQuery(query, defaultSubscriptionId), [query, defaultSubscriptionId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (preparedQuery !== query) {
|
||||
|
||||
-55
@@ -3,7 +3,6 @@ import userEvent from '@testing-library/user-event';
|
||||
|
||||
import createMockDatasource from '../../__mocks__/datasource';
|
||||
import createMockQuery from '../../__mocks__/query';
|
||||
import { AzureQueryType } from '../../dataquery.gen';
|
||||
import { createMockResourcePickerData } from '../MetricsQueryEditor/MetricsQueryEditor.test';
|
||||
|
||||
import TracesQueryEditor from './TracesQueryEditor';
|
||||
@@ -184,58 +183,4 @@ describe('TracesQueryEditor', () => {
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should not display the resource selector for exemplar type queries', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
const query = createMockQuery();
|
||||
delete query?.subscription;
|
||||
delete query?.azureTraces?.resources;
|
||||
query.queryType = AzureQueryType.TraceExemplar;
|
||||
query.azureTraces = { operationId: 'test-operation-id' };
|
||||
const onChange = jest.fn();
|
||||
|
||||
render(
|
||||
<TracesQueryEditor
|
||||
query={query}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onChange={onChange}
|
||||
setError={() => {}}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(await screen.queryByRole('button', { name: 'Select a resource' })).not.toBeInTheDocument();
|
||||
expect(await screen.getByDisplayValue('test-operation-id')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not display the resource selector for exemplar type queries', async () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
const query = createMockQuery();
|
||||
delete query?.subscription;
|
||||
delete query?.azureTraces?.resources;
|
||||
query.queryType = AzureQueryType.TraceExemplar;
|
||||
query.azureTraces = { operationId: 'test-operation-id' };
|
||||
const onChange = jest.fn();
|
||||
|
||||
render(
|
||||
<TracesQueryEditor
|
||||
query={query}
|
||||
datasource={mockDatasource}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onChange={onChange}
|
||||
setError={() => {}}
|
||||
/>
|
||||
);
|
||||
|
||||
const operationIDInput = await screen.getByDisplayValue('test-operation-id');
|
||||
await userEvent.clear(operationIDInput);
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
azureTraces: undefined,
|
||||
queryType: AzureQueryType.AzureTraces,
|
||||
query: undefined,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
+29
-31
@@ -88,37 +88,35 @@ const TracesQueryEditor = ({
|
||||
return (
|
||||
<span data-testid={selectors.components.queryEditor.tracesQueryEditor.container.input}>
|
||||
<EditorRows>
|
||||
{query.queryType !== AzureQueryType.TraceExemplar && (
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
<ResourceField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
subscriptionId={subscriptionId}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
selectableEntryTypes={[
|
||||
ResourceRowType.Subscription,
|
||||
ResourceRowType.ResourceGroup,
|
||||
ResourceRowType.Resource,
|
||||
ResourceRowType.Variable,
|
||||
]}
|
||||
resources={query.azureTraces?.resources ?? []}
|
||||
queryType="traces"
|
||||
disableRow={disableRow}
|
||||
renderAdvanced={(resources, onChange) => (
|
||||
// It's required to cast resources because the resource picker
|
||||
// specifies the type to string | AzureMonitorResource.
|
||||
// eslint-disable-next-line
|
||||
<AdvancedResourcePicker resources={resources as string[]} onChange={onChange} />
|
||||
)}
|
||||
selectionNotice={() => 'You may only choose items of the same resource type.'}
|
||||
range={range}
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
)}
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
<ResourceField
|
||||
query={query}
|
||||
datasource={datasource}
|
||||
subscriptionId={subscriptionId}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
onQueryChange={onChange}
|
||||
setError={setError}
|
||||
selectableEntryTypes={[
|
||||
ResourceRowType.Subscription,
|
||||
ResourceRowType.ResourceGroup,
|
||||
ResourceRowType.Resource,
|
||||
ResourceRowType.Variable,
|
||||
]}
|
||||
resources={query.azureTraces?.resources ?? []}
|
||||
queryType="traces"
|
||||
disableRow={disableRow}
|
||||
renderAdvanced={(resources, onChange) => (
|
||||
// It's required to cast resources because the resource picker
|
||||
// specifies the type to string | AzureMonitorResource.
|
||||
// eslint-disable-next-line
|
||||
<AdvancedResourcePicker resources={resources as string[]} onChange={onChange} />
|
||||
)}
|
||||
selectionNotice={() => 'You may only choose items of the same resource type.'}
|
||||
range={range}
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
<TraceTypeField
|
||||
|
||||
@@ -3849,11 +3849,11 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@grafana/scenes-react@npm:^5.35.0":
|
||||
version: 5.35.0
|
||||
resolution: "@grafana/scenes-react@npm:5.35.0"
|
||||
"@grafana/scenes-react@npm:5.36.3":
|
||||
version: 5.36.3
|
||||
resolution: "@grafana/scenes-react@npm:5.36.3"
|
||||
dependencies:
|
||||
"@grafana/scenes": "npm:5.35.0"
|
||||
"@grafana/scenes": "npm:5.36.3"
|
||||
lru-cache: "npm:^10.2.2"
|
||||
react-use: "npm:^17.4.0"
|
||||
peerDependencies:
|
||||
@@ -3864,13 +3864,13 @@ __metadata:
|
||||
"@grafana/ui": ^11.0.0
|
||||
react: ^18.0.0
|
||||
react-dom: ^18.0.0
|
||||
checksum: 10/f70799e627ed12464266224bc42d7dbc1ce38753dd3e8ff48cef0e14639578e03d82d178657c7a30e8b4e5062b140e0d4faa94e7a4a73d7e0263338a43edf7d7
|
||||
checksum: 10/70cdc9cdb8b5237e3e03c35e4ab1dce923be46b9bcf26901df55c6aed48a35c008c5b976e22782ddfd5a45cf4165a222648e77adcc75346b090ec49297751091
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@grafana/scenes@npm:5.35.0, @grafana/scenes@npm:^5.35.0":
|
||||
version: 5.35.0
|
||||
resolution: "@grafana/scenes@npm:5.35.0"
|
||||
"@grafana/scenes@npm:5.36.3":
|
||||
version: 5.36.3
|
||||
resolution: "@grafana/scenes@npm:5.36.3"
|
||||
dependencies:
|
||||
"@floating-ui/react": "npm:^0.26.16"
|
||||
"@leeoniya/ufuzzy": "npm:^1.0.16"
|
||||
@@ -3887,7 +3887,7 @@ __metadata:
|
||||
"@grafana/ui": ">=10.4"
|
||||
react: ^18.0.0
|
||||
react-dom: ^18.0.0
|
||||
checksum: 10/deab041b4a3b3f99ed595e58f795aa06519020f85289a9544145e0b8969be6f826e470d98dc4981da9809bf9129f0fa11e26e3f8395fbe50923d51c85554b5ed
|
||||
checksum: 10/8bc404091311b55f5316d567fc41789659fd221d38b4472ad5fa65afa224c9127e11a51daec2c07eccf941ec2eae58c0690dc312726139fdef3cf44ceb1460c9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -6830,21 +6830,21 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rsdoctor/client@npm:0.4.12":
|
||||
version: 0.4.12
|
||||
resolution: "@rsdoctor/client@npm:0.4.12"
|
||||
checksum: 10/04ae4f8de34fdf5cec5b9db2072e646f3327a7ba7ca4a96942961fa26129e715db92c28ca68bf0f6c5149ccbfb9b73aa2d2984242bf8d35bc54f827e2ccae84e
|
||||
"@rsdoctor/client@npm:0.4.13":
|
||||
version: 0.4.13
|
||||
resolution: "@rsdoctor/client@npm:0.4.13"
|
||||
checksum: 10/6eaa768eceedf76aa940fcb5980eb39f5960f4f341c13c96ecddac8abb456c8248497dfbc0d7de933d02ad837f761af93bc54d76e84dca45591a6d99f000556b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rsdoctor/core@npm:0.4.12":
|
||||
version: 0.4.12
|
||||
resolution: "@rsdoctor/core@npm:0.4.12"
|
||||
"@rsdoctor/core@npm:0.4.13":
|
||||
version: 0.4.13
|
||||
resolution: "@rsdoctor/core@npm:0.4.13"
|
||||
dependencies:
|
||||
"@rsdoctor/graph": "npm:0.4.12"
|
||||
"@rsdoctor/sdk": "npm:0.4.12"
|
||||
"@rsdoctor/types": "npm:0.4.12"
|
||||
"@rsdoctor/utils": "npm:0.4.12"
|
||||
"@rsdoctor/graph": "npm:0.4.13"
|
||||
"@rsdoctor/sdk": "npm:0.4.13"
|
||||
"@rsdoctor/types": "npm:0.4.13"
|
||||
"@rsdoctor/utils": "npm:0.4.13"
|
||||
axios: "npm:^1.7.9"
|
||||
enhanced-resolve: "npm:5.12.0"
|
||||
filesize: "npm:^10.1.6"
|
||||
@@ -6854,31 +6854,31 @@ __metadata:
|
||||
semver: "npm:^7.6.3"
|
||||
source-map: "npm:^0.7.4"
|
||||
webpack-bundle-analyzer: "npm:^4.10.2"
|
||||
checksum: 10/d77252288cec3ad9cd6389fb772125fcbdca669c0186a540a9270f2c3ac01b0620d188595e952a3683b0af4e4eef2c8816b0af71a2d87667eef7f2e47484a4b3
|
||||
checksum: 10/c105de8a0933ec7abf2104762a7db1046f0bd2c418fc67fad8315c44f22cdb5a6ec34dbcf5da24c8a698959d475f120cc598ec012870fe757fc968fbe603ced3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rsdoctor/graph@npm:0.4.12":
|
||||
version: 0.4.12
|
||||
resolution: "@rsdoctor/graph@npm:0.4.12"
|
||||
"@rsdoctor/graph@npm:0.4.13":
|
||||
version: 0.4.13
|
||||
resolution: "@rsdoctor/graph@npm:0.4.13"
|
||||
dependencies:
|
||||
"@rsdoctor/types": "npm:0.4.12"
|
||||
"@rsdoctor/utils": "npm:0.4.12"
|
||||
lodash: "npm:^4.17.21"
|
||||
"@rsdoctor/types": "npm:0.4.13"
|
||||
"@rsdoctor/utils": "npm:0.4.13"
|
||||
lodash.unionby: "npm:^4.8.0"
|
||||
socket.io: "npm:4.8.1"
|
||||
source-map: "npm:^0.7.4"
|
||||
checksum: 10/53cb9dd4c8507cf22ee6088e4fc1b5d26fb2090dee1570fd1964659d854bab33ed7ca73862a84a256280a85aa076c5e18953830af2127fb583d5eaa5274a934d
|
||||
checksum: 10/40080b9726661d51bbec931ec85376039eec9f02c6a5523eb78365dbe0c7d2f86798fa2ff7ca29c13e844c5ab686bd8b6947d064aa0caaa97b06754f8a167ea1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rsdoctor/sdk@npm:0.4.12":
|
||||
version: 0.4.12
|
||||
resolution: "@rsdoctor/sdk@npm:0.4.12"
|
||||
"@rsdoctor/sdk@npm:0.4.13":
|
||||
version: 0.4.13
|
||||
resolution: "@rsdoctor/sdk@npm:0.4.13"
|
||||
dependencies:
|
||||
"@rsdoctor/client": "npm:0.4.12"
|
||||
"@rsdoctor/graph": "npm:0.4.12"
|
||||
"@rsdoctor/types": "npm:0.4.12"
|
||||
"@rsdoctor/utils": "npm:0.4.12"
|
||||
"@rsdoctor/client": "npm:0.4.13"
|
||||
"@rsdoctor/graph": "npm:0.4.13"
|
||||
"@rsdoctor/types": "npm:0.4.13"
|
||||
"@rsdoctor/utils": "npm:0.4.13"
|
||||
"@types/fs-extra": "npm:^11.0.4"
|
||||
body-parser: "npm:1.20.3"
|
||||
cors: "npm:2.8.5"
|
||||
@@ -6891,13 +6891,13 @@ __metadata:
|
||||
socket.io: "npm:4.8.1"
|
||||
source-map: "npm:^0.7.4"
|
||||
tapable: "npm:2.2.1"
|
||||
checksum: 10/a0f20c0410e44fd9f7428fee39c45abf493144e73aa910d9c8f34760461d36d1144856c9ea3078afee2282817acb8bffb82bad9ec8443279dde5bf14875df495
|
||||
checksum: 10/017cfe877952038a573d6e498dc4c5da1b0754bd5e50ccaf02e5263581a3ca901600b77b59cd9556cc9baefef8c37b0ccf5e7364342fb080c14e4b72d6005e97
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rsdoctor/types@npm:0.4.12":
|
||||
version: 0.4.12
|
||||
resolution: "@rsdoctor/types@npm:0.4.12"
|
||||
"@rsdoctor/types@npm:0.4.13":
|
||||
version: 0.4.13
|
||||
resolution: "@rsdoctor/types@npm:0.4.13"
|
||||
dependencies:
|
||||
"@types/connect": "npm:3.4.38"
|
||||
"@types/estree": "npm:1.0.5"
|
||||
@@ -6909,16 +6909,16 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
"@rspack/core":
|
||||
optional: true
|
||||
checksum: 10/41cbfd7eb36999e79eed49ebc49e5665c5c8c68070f2981558cdf110fdcc60d97b2e653ead5c12cf9ebcef8b7de98b42bc20cf15258a42a3f2ae5a7b75aa8379
|
||||
checksum: 10/a6e51bfc9e88779e9043d6f75420604f2839bbd7f7608a58556374ae74e7191b9a47872f9a426cf76e2e7756c8724550237398e093a415f6b8fbab84fae2dde5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rsdoctor/utils@npm:0.4.12":
|
||||
version: 0.4.12
|
||||
resolution: "@rsdoctor/utils@npm:0.4.12"
|
||||
"@rsdoctor/utils@npm:0.4.13":
|
||||
version: 0.4.13
|
||||
resolution: "@rsdoctor/utils@npm:0.4.13"
|
||||
dependencies:
|
||||
"@babel/code-frame": "npm:7.25.7"
|
||||
"@rsdoctor/types": "npm:0.4.12"
|
||||
"@rsdoctor/types": "npm:0.4.13"
|
||||
"@types/estree": "npm:1.0.5"
|
||||
acorn: "npm:^8.10.0"
|
||||
acorn-import-assertions: "npm:1.9.0"
|
||||
@@ -6932,27 +6932,26 @@ __metadata:
|
||||
get-port: "npm:5.1.1"
|
||||
json-stream-stringify: "npm:3.0.1"
|
||||
lines-and-columns: "npm:2.0.4"
|
||||
lodash: "npm:^4.17.21"
|
||||
rslog: "npm:^1.2.3"
|
||||
strip-ansi: "npm:^6.0.1"
|
||||
checksum: 10/f0640c4cabacbf0c65cb2c2bc36791c9f07c9e4ea7917636f5480c193043a7f16cf276734a91bcd9309ec3389987e586e3a557e99266d758affdb76a2efb6a5a
|
||||
checksum: 10/e102670b4432ee0ed47cda15eef8fc90e703451f3d8c7efe8d7e4817fe92b2702b33a303b156fd5409d6abcc0c5712be3a81817dd21f09dd9a6bec5ecf1c72ef
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@rsdoctor/webpack-plugin@npm:^0.4.6":
|
||||
version: 0.4.12
|
||||
resolution: "@rsdoctor/webpack-plugin@npm:0.4.12"
|
||||
version: 0.4.13
|
||||
resolution: "@rsdoctor/webpack-plugin@npm:0.4.13"
|
||||
dependencies:
|
||||
"@rsdoctor/core": "npm:0.4.12"
|
||||
"@rsdoctor/graph": "npm:0.4.12"
|
||||
"@rsdoctor/sdk": "npm:0.4.12"
|
||||
"@rsdoctor/types": "npm:0.4.12"
|
||||
"@rsdoctor/utils": "npm:0.4.12"
|
||||
"@rsdoctor/core": "npm:0.4.13"
|
||||
"@rsdoctor/graph": "npm:0.4.13"
|
||||
"@rsdoctor/sdk": "npm:0.4.13"
|
||||
"@rsdoctor/types": "npm:0.4.13"
|
||||
"@rsdoctor/utils": "npm:0.4.13"
|
||||
fs-extra: "npm:^11.1.1"
|
||||
lodash: "npm:^4.17.21"
|
||||
peerDependencies:
|
||||
webpack: 5.x
|
||||
checksum: 10/e2bfc961f39bfa27e60ada29cccf2734db59062d78007cedaec3c794f8c39864c6f0e487879f565fcad100ec970d3e320c5927315fc2ed00d55a00924891b6f3
|
||||
checksum: 10/f244ef8f1b28192cb766d66099e6999ba5a042d19bd1ab29f023f4844807a6a444e26a73b31a077e03c600979d5f10ec70fd1a42e9af9766b6f22241f124b990
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -17634,8 +17633,8 @@ __metadata:
|
||||
"@grafana/prometheus": "workspace:*"
|
||||
"@grafana/runtime": "workspace:*"
|
||||
"@grafana/saga-icons": "workspace:*"
|
||||
"@grafana/scenes": "npm:^5.35.0"
|
||||
"@grafana/scenes-react": "npm:^5.35.0"
|
||||
"@grafana/scenes": "npm:5.36.3"
|
||||
"@grafana/scenes-react": "npm:5.36.3"
|
||||
"@grafana/schema": "workspace:*"
|
||||
"@grafana/sql": "workspace:*"
|
||||
"@grafana/tsconfig": "npm:^2.0.0"
|
||||
@@ -21353,6 +21352,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash.unionby@npm:^4.8.0":
|
||||
version: 4.8.0
|
||||
resolution: "lodash.unionby@npm:4.8.0"
|
||||
checksum: 10/246eaf5b24cb63387abfbaab3a23883c500c56eb0d4b364c48739873b55a3c3131688f35926de64046b74377937ddb344a84282ebc0528b9dc3be5866216a91e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash.uniq@npm:^4.5.0":
|
||||
version: 4.5.0
|
||||
resolution: "lodash.uniq@npm:4.5.0"
|
||||
|
||||
Reference in new issue
Block a user