diff --git a/.github/bot.md b/.github/bot.md index 6eae334f4fa..e4e61b13e4b 100644 --- a/.github/bot.md +++ b/.github/bot.md @@ -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 diff --git a/docs/sources/administration/data-source-management/teamlbac/_index.md b/docs/sources/administration/data-source-management/teamlbac/_index.md index 48681fd4511..ac801cf9d18 100644 --- a/docs/sources/administration/data-source-management/teamlbac/_index.md +++ b/docs/sources/administration/data-source-management/teamlbac/_index.md @@ -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//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. diff --git a/package.json b/package.json index 5bb1f860f03..f69d783a8db 100644 --- a/package.json +++ b/package.json @@ -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:*", diff --git a/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go b/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go index 93db9a8ebbd..c8c9592804f 100644 --- a/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go +++ b/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go @@ -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 diff --git a/pkg/tsdb/azuremonitor/loganalytics/traces_test.go b/pkg/tsdb/azuremonitor/loganalytics/traces_test.go index c93bcc26703..7854bd880f3 100644 --- a/pkg/tsdb/azuremonitor/loganalytics/traces_test.go +++ b/pkg/tsdb/azuremonitor/loganalytics/traces_test.go @@ -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) } }) diff --git a/public/app/features/dashboard-scene/scene/PanelSearchLayout.tsx b/public/app/features/dashboard-scene/scene/PanelSearchLayout.tsx index 56ab7f8211c..780fab43b99 100644 --- a/public/app/features/dashboard-scene/scene/PanelSearchLayout.tsx +++ b/public/app/features/dashboard-scene/scene/PanelSearchLayout.tsx @@ -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 ; } diff --git a/public/app/features/dashboard-scene/utils/utils.ts b/public/app/features/dashboard-scene/utils/utils.ts index 3e34ec23362..3a26fc19170 100644 --- a/public/app/features/dashboard-scene/utils/utils.ts +++ b/public/app/features/dashboard-scene/utils/utils.ts @@ -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. diff --git a/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.test.tsx b/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.test.tsx index 18cd93f66da..8099a03cb02 100644 --- a/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.test.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.test.tsx @@ -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( {}} />); + + 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(); + }); }); diff --git a/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.tsx b/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.tsx index eaecfe57345..3061a5183fc 100644 --- a/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.tsx @@ -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 = diff --git a/public/app/plugins/datasource/azuremonitor/components/QueryEditor/usePreparedQuery.ts b/public/app/plugins/datasource/azuremonitor/components/QueryEditor/usePreparedQuery.ts index 3361a7e77dc..8b12487cd39 100644 --- a/public/app/plugins/datasource/azuremonitor/components/QueryEditor/usePreparedQuery.ts +++ b/public/app/plugins/datasource/azuremonitor/components/QueryEditor/usePreparedQuery.ts @@ -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) { diff --git a/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/TracesQueryEditor.test.tsx b/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/TracesQueryEditor.test.tsx index 8a0b647b19a..ce84d546d58 100644 --- a/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/TracesQueryEditor.test.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/TracesQueryEditor.test.tsx @@ -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( - {}} - /> - ); - - 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( - {}} - /> - ); - - const operationIDInput = await screen.getByDisplayValue('test-operation-id'); - await userEvent.clear(operationIDInput); - - expect(onChange).toHaveBeenCalledWith( - expect.objectContaining({ - azureTraces: undefined, - queryType: AzureQueryType.AzureTraces, - query: undefined, - }) - ); - }); }); diff --git a/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/TracesQueryEditor.tsx b/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/TracesQueryEditor.tsx index 09deeab32e8..7ed37f8fa21 100644 --- a/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/TracesQueryEditor.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/TracesQueryEditor.tsx @@ -88,37 +88,35 @@ const TracesQueryEditor = ({ return ( - {query.queryType !== AzureQueryType.TraceExemplar && ( - - - ( - // It's required to cast resources because the resource picker - // specifies the type to string | AzureMonitorResource. - // eslint-disable-next-line - - )} - selectionNotice={() => 'You may only choose items of the same resource type.'} - range={range} - /> - - - )} + + + ( + // It's required to cast resources because the resource picker + // specifies the type to string | AzureMonitorResource. + // eslint-disable-next-line + + )} + selectionNotice={() => 'You may only choose items of the same resource type.'} + range={range} + /> + + =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"