[release-12.0.5] Azure: Fix logs editor rendering (#109667)

* Azure: Fix logs editor rendering (#109491)

* Fix logs editor rendering

- Add test

* Type fixes

* Fix schema conditions and add test

* Fix lint

* Update public/app/plugins/datasource/azuremonitor/components/LogsQueryEditor/TimeManagement.tsx

Co-authored-by: Adam Yeats <16296989+adamyeats@users.noreply.github.com>

* Lint

---------

Co-authored-by: Adam Yeats <16296989+adamyeats@users.noreply.github.com>
(cherry picked from commit b34642b188)

# Conflicts:
#	public/app/plugins/datasource/azuremonitor/components/LogsQueryEditor/LogsQueryEditor.test.tsx

* Mock error and warn
This commit is contained in:
Andreas Christou 2025-08-18 18:37:05 +02:00 committed by GitHub
parent 560f875837
commit ab1e43b8b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 171 additions and 28 deletions

View File

@ -38,7 +38,7 @@ interface LogsQueryBuilderProps {
query: AzureMonitorQuery; query: AzureMonitorQuery;
basicLogsEnabled: boolean; basicLogsEnabled: boolean;
onQueryChange: (newQuery: AzureMonitorQuery) => void; onQueryChange: (newQuery: AzureMonitorQuery) => void;
schema: EngineSchema; schema?: EngineSchema;
templateVariableOptions: SelectableValue<string>; templateVariableOptions: SelectableValue<string>;
datasource: Datasource; datasource: Datasource;
timeRange?: TimeRange; timeRange?: TimeRange;

View File

@ -6,6 +6,7 @@ import { dateTime, LoadingState } from '@grafana/data';
import createMockDatasource from '../../__mocks__/datasource'; import createMockDatasource from '../../__mocks__/datasource';
import createMockQuery from '../../__mocks__/query'; import createMockQuery from '../../__mocks__/query';
import { ResultFormat } from '../../dataquery.gen'; import { ResultFormat } from '../../dataquery.gen';
import { EngineSchema } from '../../types/types';
import { createMockResourcePickerData } from '../MetricsQueryEditor/MetricsQueryEditor.test'; import { createMockResourcePickerData } from '../MetricsQueryEditor/MetricsQueryEditor.test';
import LogsQueryEditor from './LogsQueryEditor'; import LogsQueryEditor from './LogsQueryEditor';
@ -628,4 +629,130 @@ describe('LogsQueryEditor', () => {
expect(onChange).toHaveBeenCalledWith(newQuery); expect(onChange).toHaveBeenCalledWith(newQuery);
}); });
}); });
describe('schema loading and auto-completion', () => {
it('loads schema and table plans when resources change and builder mode is set', async () => {
// Mock these as we expect Kusto to complain about workers
jest.spyOn(console, 'warn').mockImplementation();
jest.spyOn(console, 'error').mockImplementation();
const mockSchema: EngineSchema = {
clusterType: 'Engine',
cluster: {
connectionString:
'/subscriptions/subscriptionId/resourceGroups/resourceGroup/providers/Microsoft.OperationalInsights/workspaces/la-workspace',
databases: [
{
name: '/subscriptions/subscriptionId/resourceGroups/resourceGroup/providers/Microsoft.OperationalInsights/workspaces/la-workspace',
tables: [
{
columns: [
{
description: '',
isPreferredFacet: false,
name: 'TenantId',
type: 'string',
},
{
description: 'Date and time when dependency call was recorded.',
isPreferredFacet: false,
name: 'TimeGenerated',
type: 'datetime',
},
],
description: 'Application Insights dependencies.',
id: 'AppDependencies',
name: 'AppDependencies',
timespanColumn: 'TimeGenerated',
hasData: true,
related: {
solutions: [],
functions: [],
categories: [],
},
},
],
functions: [],
majorVersion: 0,
minorVersion: 0,
entityGroups: [],
},
],
},
database: {
name: '/subscriptions/subscriptionId/resourceGroups/resourceGroup/providers/Microsoft.OperationalInsights/workspaces/la-workspace',
tables: [
{
columns: [
{
description: '',
isPreferredFacet: false,
name: 'TenantId',
type: 'string',
},
{
description: 'Date and time when dependency call was recorded.',
isPreferredFacet: false,
name: 'TimeGenerated',
type: 'datetime',
},
],
description: 'Application Insights dependencies.',
id: 'AppDependencies',
name: 'AppDependencies',
timespanColumn: 'TimeGenerated',
hasData: true,
related: {
solutions: [],
functions: [],
categories: [],
},
},
],
functions: [],
majorVersion: 0,
minorVersion: 0,
entityGroups: [],
},
};
const mockDatasource = createMockDatasource();
mockDatasource.azureLogAnalyticsDatasource.getKustoSchema = jest.fn().mockResolvedValue(mockSchema);
// @ts-ignore: forcibly attach for test
mockDatasource.azureMonitorDatasource.getWorkspaceTablePlan = jest.fn().mockResolvedValue('plan');
const query = createMockQuery({
azureLogAnalytics: {
resources: [
'/subscriptions/def-456/resourceGroups/dev-3/providers/microsoft.operationalinsights/workspaces/la-workspace',
],
mode: require('../../dataquery.gen').LogsEditorMode.Builder,
},
});
const onChange = jest.fn();
const onQueryChange = jest.fn();
await act(async () => {
render(
<LogsQueryEditor
query={query}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onChange={onChange}
onQueryChange={onQueryChange}
setError={() => {}}
basicLogsEnabled={true}
/>
);
});
await waitFor(() => {
expect(mockDatasource.azureLogAnalyticsDatasource.getKustoSchema).toHaveBeenCalledWith(
query.azureLogAnalytics?.resources?.[0]
);
});
expect(mockDatasource.azureMonitorDatasource.getWorkspaceTablePlan).toHaveBeenCalledTimes(1);
expect(mockDatasource.azureMonitorDatasource.getWorkspaceTablePlan).toHaveBeenCalledWith(
query.azureLogAnalytics?.resources,
'AppDependencies'
);
});
});
}); });

View File

@ -112,11 +112,9 @@ const LogsQueryEditor = ({
if (schema.database?.tables) { if (schema.database?.tables) {
schema.database.tables = t; schema.database.tables = t;
} }
setSchema(schema);
}); });
} else {
setSchema(schema);
} }
setSchema(schema);
setIsLoadingSchema(false); setIsLoadingSchema(false);
}); });
} }
@ -285,7 +283,7 @@ const LogsQueryEditor = ({
!!config.featureToggles.azureMonitorLogsBuilderEditor ? ( !!config.featureToggles.azureMonitorLogsBuilderEditor ? (
<LogsQueryBuilder <LogsQueryBuilder
query={query} query={query}
schema={schema!} schema={schema}
basicLogsEnabled={basicLogsEnabled} basicLogsEnabled={basicLogsEnabled}
onQueryChange={onQueryChange} onQueryChange={onQueryChange}
templateVariableOptions={templateVariableOptions} templateVariableOptions={templateVariableOptions}

View File

@ -60,6 +60,25 @@ describe('LogsQueryEditor.TimeManagement', () => {
); );
}); });
it('should render correctly even if no tables are in the schema', async () => {
const mockDatasource = createMockDatasource();
const query = createMockQuery({ azureLogAnalytics: { timeColumn: undefined } });
const onChange = jest.fn();
render(
<TimeManagement
query={query}
datasource={mockDatasource}
variableOptionGroup={variableOptionGroup}
onQueryChange={onChange}
setError={() => {}}
schema={FakeSchemaData.getLogAnalyticsFakeEngineSchema([])}
/>
);
expect(screen.getByText('Time-range')).toBeInTheDocument();
});
it('should render the default value if no time columns exist', async () => { it('should render the default value if no time columns exist', async () => {
const mockDatasource = createMockDatasource(); const mockDatasource = createMockDatasource();
const query = createMockQuery(); const query = createMockQuery();

View File

@ -20,7 +20,7 @@ export function TimeManagement({ query, onQueryChange: onChange, schema }: Azure
const timeColumnsSet: Set<string> = new Set(); const timeColumnsSet: Set<string> = new Set();
const defaultColumnsMap: Map<string, SelectableValue> = new Map(); const defaultColumnsMap: Map<string, SelectableValue> = new Map();
const db = schema.database; const db = schema.database;
if (db) { if (db && db?.tables?.length > 0) {
for (const table of db.tables) { for (const table of db.tables) {
const cols = table.columns.reduce<SelectableValue[]>((prev, curr, i) => { const cols = table.columns.reduce<SelectableValue[]>((prev, curr, i) => {
if (curr.type === 'datetime') { if (curr.type === 'datetime') {
@ -39,11 +39,9 @@ export function TimeManagement({ query, onQueryChange: onChange, schema }: Azure
}); });
} }
} }
}
setTimeColumns(timeColumnOptions); setTimeColumns(timeColumnOptions);
const defaultColumns = Array.from(defaultColumnsMap.values()); const defaultColumns = Array.from(defaultColumnsMap.values());
setDefaultTimeColumns(defaultColumns); setDefaultTimeColumns(defaultColumns);
// Set default value // Set default value
if ( if (
!query.azureLogAnalytics.timeColumn || !query.azureLogAnalytics.timeColumn ||
@ -64,6 +62,7 @@ export function TimeManagement({ query, onQueryChange: onChange, schema }: Azure
} }
} }
} }
}
}, [schema, query.azureLogAnalytics?.dashboardTime, query.azureLogAnalytics?.timeColumn, setDefaultColumn]); }, [schema, query.azureLogAnalytics?.dashboardTime, query.azureLogAnalytics?.timeColumn, setDefaultColumn]);
const handleTimeColumnChange = useCallback( const handleTimeColumnChange = useCallback(