mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AzureMonitor: Convert Logs to React (#32315)
* Convert Logs to React * copy changes * fix effect deps * tests for logs * remove any from test * Update QueryEditor.tsx
This commit is contained in:
parent
7ea58f9cf5
commit
d2bdb4ed41
@ -17,6 +17,8 @@ export default function createMockDatasource() {
|
|||||||
getSubscriptions: jest.fn().mockResolvedValueOnce([]),
|
getSubscriptions: jest.fn().mockResolvedValueOnce([]),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getAzureLogAnalyticsWorkspaces: jest.fn().mockResolvedValueOnce([]),
|
||||||
|
|
||||||
getResourceGroups: jest.fn().mockResolvedValueOnce([]),
|
getResourceGroups: jest.fn().mockResolvedValueOnce([]),
|
||||||
getMetricDefinitions: jest.fn().mockResolvedValueOnce([]),
|
getMetricDefinitions: jest.fn().mockResolvedValueOnce([]),
|
||||||
getResourceNames: jest.fn().mockResolvedValueOnce([]),
|
getResourceNames: jest.fn().mockResolvedValueOnce([]),
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
import { SelectableValue } from '@grafana/data';
|
||||||
|
import { Select } from '@grafana/ui';
|
||||||
|
import React, { useCallback, useMemo } from 'react';
|
||||||
|
import { AzureMonitorOption, AzureQueryEditorFieldProps, AzureResultFormat } from '../../types';
|
||||||
|
import { findOption } from '../../utils/common';
|
||||||
|
import { Field } from '../Field';
|
||||||
|
|
||||||
|
const FORMAT_OPTIONS: Array<AzureMonitorOption<AzureResultFormat>> = [
|
||||||
|
{ label: 'Time series', value: 'time_series' },
|
||||||
|
{ label: 'Table', value: 'table' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const FormatAsField: React.FC<AzureQueryEditorFieldProps> = ({ query, variableOptionGroup, onQueryChange }) => {
|
||||||
|
const options = useMemo(() => [...FORMAT_OPTIONS, variableOptionGroup], [variableOptionGroup]);
|
||||||
|
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(change: SelectableValue<AzureResultFormat>) => {
|
||||||
|
const { value } = change;
|
||||||
|
if (!value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onQueryChange({
|
||||||
|
...query,
|
||||||
|
azureLogAnalytics: {
|
||||||
|
...query.azureLogAnalytics,
|
||||||
|
resultFormat: value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[onQueryChange, query]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Field label="Format as">
|
||||||
|
<Select
|
||||||
|
inputId="azure-monitor-logs-workspaces-field"
|
||||||
|
value={findOption(FORMAT_OPTIONS, query.azureLogAnalytics.resultFormat)}
|
||||||
|
onChange={handleChange}
|
||||||
|
options={options}
|
||||||
|
width={38}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FormatAsField;
|
@ -0,0 +1,69 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { AzureMonitorErrorish, AzureMonitorOption, AzureMonitorQuery } from '../../types';
|
||||||
|
import Datasource from '../../datasource';
|
||||||
|
import { InlineFieldRow } from '@grafana/ui';
|
||||||
|
import SubscriptionField from '../SubscriptionField';
|
||||||
|
import WorkspaceField from './WorkspaceField';
|
||||||
|
import QueryField from './QueryField';
|
||||||
|
import FormatAsField from './FormatAsField';
|
||||||
|
|
||||||
|
interface LogsQueryEditorProps {
|
||||||
|
query: AzureMonitorQuery;
|
||||||
|
datasource: Datasource;
|
||||||
|
subscriptionId: string;
|
||||||
|
onChange: (newQuery: AzureMonitorQuery) => void;
|
||||||
|
variableOptionGroup: { label: string; options: AzureMonitorOption[] };
|
||||||
|
setError: (source: string, error: AzureMonitorErrorish | undefined) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LogsQueryEditor: React.FC<LogsQueryEditorProps> = ({
|
||||||
|
query,
|
||||||
|
datasource,
|
||||||
|
subscriptionId,
|
||||||
|
variableOptionGroup,
|
||||||
|
onChange,
|
||||||
|
setError,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div data-testid="azure-monitor-logs-query-editor">
|
||||||
|
<InlineFieldRow>
|
||||||
|
<SubscriptionField
|
||||||
|
query={query}
|
||||||
|
datasource={datasource}
|
||||||
|
subscriptionId={subscriptionId}
|
||||||
|
variableOptionGroup={variableOptionGroup}
|
||||||
|
onQueryChange={onChange}
|
||||||
|
setError={setError}
|
||||||
|
/>
|
||||||
|
<WorkspaceField
|
||||||
|
query={query}
|
||||||
|
datasource={datasource}
|
||||||
|
subscriptionId={subscriptionId}
|
||||||
|
variableOptionGroup={variableOptionGroup}
|
||||||
|
onQueryChange={onChange}
|
||||||
|
setError={setError}
|
||||||
|
/>
|
||||||
|
</InlineFieldRow>
|
||||||
|
|
||||||
|
<QueryField
|
||||||
|
query={query}
|
||||||
|
datasource={datasource}
|
||||||
|
subscriptionId={subscriptionId}
|
||||||
|
variableOptionGroup={variableOptionGroup}
|
||||||
|
onQueryChange={onChange}
|
||||||
|
setError={setError}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormatAsField
|
||||||
|
query={query}
|
||||||
|
datasource={datasource}
|
||||||
|
subscriptionId={subscriptionId}
|
||||||
|
variableOptionGroup={variableOptionGroup}
|
||||||
|
onQueryChange={onChange}
|
||||||
|
setError={setError}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LogsQueryEditor;
|
@ -0,0 +1,32 @@
|
|||||||
|
import { CodeEditor } from '@grafana/ui';
|
||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { AzureQueryEditorFieldProps } from '../../types';
|
||||||
|
|
||||||
|
const QueryField: React.FC<AzureQueryEditorFieldProps> = ({ query, onQueryChange }) => {
|
||||||
|
const onChange = useCallback(
|
||||||
|
(newQuery: string) => {
|
||||||
|
onQueryChange({
|
||||||
|
...query,
|
||||||
|
azureLogAnalytics: {
|
||||||
|
...query.azureLogAnalytics,
|
||||||
|
query: newQuery,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[onQueryChange, query]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CodeEditor
|
||||||
|
value={query.azureLogAnalytics.query}
|
||||||
|
language="kql"
|
||||||
|
height={200}
|
||||||
|
width="100%"
|
||||||
|
showMiniMap={false}
|
||||||
|
onBlur={onChange}
|
||||||
|
onSave={onChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default QueryField;
|
@ -0,0 +1,64 @@
|
|||||||
|
import { SelectableValue } from '@grafana/data';
|
||||||
|
import { Select } from '@grafana/ui';
|
||||||
|
import React, { useEffect, useState, useMemo, useCallback } from 'react';
|
||||||
|
import { AzureQueryEditorFieldProps, AzureMonitorOption } from '../../types';
|
||||||
|
import { findOption, toOption } from '../../utils/common';
|
||||||
|
import { Field } from '../Field';
|
||||||
|
|
||||||
|
const ERROR_SOURCE = 'logs-workspaces';
|
||||||
|
const WorkspaceField: React.FC<AzureQueryEditorFieldProps> = ({
|
||||||
|
query,
|
||||||
|
datasource,
|
||||||
|
subscriptionId,
|
||||||
|
variableOptionGroup,
|
||||||
|
onQueryChange,
|
||||||
|
setError,
|
||||||
|
}) => {
|
||||||
|
const [workspaces, setWorkspaces] = useState<AzureMonitorOption[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!subscriptionId) {
|
||||||
|
workspaces.length > 0 && setWorkspaces([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource
|
||||||
|
.getAzureLogAnalyticsWorkspaces(subscriptionId)
|
||||||
|
.then((results) => {
|
||||||
|
setWorkspaces(results.map(toOption));
|
||||||
|
})
|
||||||
|
.catch((err) => setError(ERROR_SOURCE, err));
|
||||||
|
}, [datasource, setError, subscriptionId, workspaces.length]);
|
||||||
|
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(change: SelectableValue<string>) => {
|
||||||
|
if (!change.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onQueryChange({
|
||||||
|
...query,
|
||||||
|
azureLogAnalytics: {
|
||||||
|
...query.azureLogAnalytics,
|
||||||
|
workspace: change.value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[onQueryChange, query]
|
||||||
|
);
|
||||||
|
|
||||||
|
const options = useMemo(() => [...workspaces, variableOptionGroup], [workspaces, variableOptionGroup]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Field label="Workspace">
|
||||||
|
<Select
|
||||||
|
inputId="azure-monitor-logs-workspaces-field"
|
||||||
|
value={findOption(workspaces, query.azureLogAnalytics.workspace)}
|
||||||
|
onChange={handleChange}
|
||||||
|
options={options}
|
||||||
|
width={38}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WorkspaceField;
|
@ -0,0 +1 @@
|
|||||||
|
export { default } from './LogsQueryEditor';
|
@ -26,7 +26,7 @@ const LegendFormatField: React.FC<AzureQueryEditorFieldProps> = ({ onQueryChange
|
|||||||
}, [onQueryChange, query, value]);
|
}, [onQueryChange, query, value]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Field label="Legend Format">
|
<Field label="Legend format">
|
||||||
<Input
|
<Input
|
||||||
id="azure-monitor-metrics-legend-field"
|
id="azure-monitor-metrics-legend-field"
|
||||||
placeholder="Alias patterns"
|
placeholder="Alias patterns"
|
||||||
|
@ -65,7 +65,7 @@ const MetricNamespaceField: React.FC<AzureQueryEditorFieldProps> = ({
|
|||||||
const options = useMemo(() => [...metricNamespaces, variableOptionGroup], [metricNamespaces, variableOptionGroup]);
|
const options = useMemo(() => [...metricNamespaces, variableOptionGroup], [metricNamespaces, variableOptionGroup]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Field label="Metric Namespace">
|
<Field label="Metric namespace">
|
||||||
<Select
|
<Select
|
||||||
inputId="azure-monitor-metrics-metric-namespace-field"
|
inputId="azure-monitor-metrics-metric-namespace-field"
|
||||||
value={findOption(metricNamespaces, query.azureMonitor.metricNamespace)}
|
value={findOption(metricNamespaces, query.azureMonitor.metricNamespace)}
|
||||||
|
@ -59,7 +59,7 @@ const ResourceGroupsField: React.FC<AzureQueryEditorFieldProps> = ({
|
|||||||
const options = useMemo(() => [...resourceGroups, variableOptionGroup], [resourceGroups, variableOptionGroup]);
|
const options = useMemo(() => [...resourceGroups, variableOptionGroup], [resourceGroups, variableOptionGroup]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Field label="Resource Group">
|
<Field label="Resource group">
|
||||||
<Select
|
<Select
|
||||||
inputId="azure-monitor-metrics-resource-group-field"
|
inputId="azure-monitor-metrics-resource-group-field"
|
||||||
value={findOption(resourceGroups, query.azureMonitor.resourceGroup)}
|
value={findOption(resourceGroups, query.azureMonitor.resourceGroup)}
|
||||||
|
@ -58,7 +58,7 @@ const ResourceNameField: React.FC<AzureQueryEditorFieldProps> = ({
|
|||||||
|
|
||||||
const selectedResourceNameValue = findOption(resourceNames, query.azureMonitor.resourceName);
|
const selectedResourceNameValue = findOption(resourceNames, query.azureMonitor.resourceName);
|
||||||
return (
|
return (
|
||||||
<Field label="Resource Name">
|
<Field label="Resource name">
|
||||||
<Select
|
<Select
|
||||||
inputId="azure-monitor-metrics-resource-name-field"
|
inputId="azure-monitor-metrics-resource-name-field"
|
||||||
value={selectedResourceNameValue}
|
value={selectedResourceNameValue}
|
||||||
|
@ -55,7 +55,7 @@ const TimeGrainField: React.FC<TimeGrainFieldProps> = ({
|
|||||||
}, [timeGrainOptions, variableOptionGroup]);
|
}, [timeGrainOptions, variableOptionGroup]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Field label="Time Grain">
|
<Field label="Time grain">
|
||||||
<Select
|
<Select
|
||||||
inputId="azure-monitor-metrics-time-grain-field"
|
inputId="azure-monitor-metrics-time-grain-field"
|
||||||
value={findOption(timeGrainOptions, query.azureMonitor.timeGrain)}
|
value={findOption(timeGrainOptions, query.azureMonitor.timeGrain)}
|
||||||
|
@ -8,6 +8,15 @@ import createMockQuery from '../../__mocks__/query';
|
|||||||
import createMockDatasource from '../../__mocks__/datasource';
|
import createMockDatasource from '../../__mocks__/datasource';
|
||||||
import { AzureQueryType } from '../../types';
|
import { AzureQueryType } from '../../types';
|
||||||
import { invalidNamespaceError } from '../../__mocks__/errors';
|
import { invalidNamespaceError } from '../../__mocks__/errors';
|
||||||
|
import * as ui from '@grafana/ui';
|
||||||
|
|
||||||
|
// Have to mock CodeEditor because it doesnt seem to work in tests???
|
||||||
|
jest.mock('@grafana/ui', () => ({
|
||||||
|
...jest.requireActual<typeof ui>('@grafana/ui'),
|
||||||
|
CodeEditor: function CodeEditor({ value }: { value: string }) {
|
||||||
|
return <pre>{value}</pre>;
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
const variableOptionGroup = {
|
const variableOptionGroup = {
|
||||||
label: 'Template variables',
|
label: 'Template variables',
|
||||||
@ -17,9 +26,14 @@ const variableOptionGroup = {
|
|||||||
describe('Azure Monitor QueryEditor', () => {
|
describe('Azure Monitor QueryEditor', () => {
|
||||||
it('renders the Metrics query editor when the query type is Metrics', async () => {
|
it('renders the Metrics query editor when the query type is Metrics', async () => {
|
||||||
const mockDatasource = createMockDatasource();
|
const mockDatasource = createMockDatasource();
|
||||||
|
const mockQuery = {
|
||||||
|
...createMockQuery(),
|
||||||
|
queryType: AzureQueryType.AzureMonitor,
|
||||||
|
};
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
query={createMockQuery()}
|
query={mockQuery}
|
||||||
datasource={mockDatasource}
|
datasource={mockDatasource}
|
||||||
variableOptionGroup={variableOptionGroup}
|
variableOptionGroup={variableOptionGroup}
|
||||||
onChange={() => {}}
|
onChange={() => {}}
|
||||||
@ -28,22 +42,22 @@ describe('Azure Monitor QueryEditor', () => {
|
|||||||
await waitFor(() => expect(screen.getByTestId('azure-monitor-metrics-query-editor')).toBeInTheDocument());
|
await waitFor(() => expect(screen.getByTestId('azure-monitor-metrics-query-editor')).toBeInTheDocument());
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not render the Metrics query editor when the query type isn't Metrics", async () => {
|
it('renders the Metrics query editor when the query type is Metrics', async () => {
|
||||||
const mockDatasource = createMockDatasource();
|
const mockDatasource = createMockDatasource();
|
||||||
const mockQuery = createMockQuery();
|
const mockQuery = {
|
||||||
const logsMockQuery = {
|
...createMockQuery(),
|
||||||
...mockQuery,
|
|
||||||
queryType: AzureQueryType.LogAnalytics,
|
queryType: AzureQueryType.LogAnalytics,
|
||||||
};
|
};
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
query={logsMockQuery}
|
query={mockQuery}
|
||||||
datasource={mockDatasource}
|
datasource={mockDatasource}
|
||||||
variableOptionGroup={variableOptionGroup}
|
variableOptionGroup={variableOptionGroup}
|
||||||
onChange={() => {}}
|
onChange={() => {}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
await waitFor(() => expect(screen.queryByTestId('azure-monitor-metrics-query-editor')).not.toBeInTheDocument());
|
await waitFor(() => expect(screen.queryByTestId('azure-monitor-logs-query-editor')).toBeInTheDocument());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('changes the query type when selected', async () => {
|
it('changes the query type when selected', async () => {
|
||||||
|
@ -5,6 +5,7 @@ import { AzureMonitorQuery, AzureQueryType, AzureMonitorOption, AzureMonitorErro
|
|||||||
import MetricsQueryEditor from '../MetricsQueryEditor';
|
import MetricsQueryEditor from '../MetricsQueryEditor';
|
||||||
import QueryTypeField from './QueryTypeField';
|
import QueryTypeField from './QueryTypeField';
|
||||||
import useLastError from '../../utils/useLastError';
|
import useLastError from '../../utils/useLastError';
|
||||||
|
import LogsQueryEditor from '../LogsQueryEditor';
|
||||||
|
|
||||||
interface BaseQueryEditorProps {
|
interface BaseQueryEditorProps {
|
||||||
query: AzureMonitorQuery;
|
query: AzureMonitorQuery;
|
||||||
@ -70,6 +71,18 @@ const EditorForQueryType: React.FC<EditorForQueryTypeProps> = ({
|
|||||||
setError={setError}
|
setError={setError}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
case AzureQueryType.LogAnalytics:
|
||||||
|
return (
|
||||||
|
<LogsQueryEditor
|
||||||
|
subscriptionId={subscriptionId}
|
||||||
|
query={query}
|
||||||
|
datasource={datasource}
|
||||||
|
onChange={onChange}
|
||||||
|
variableOptionGroup={variableOptionGroup}
|
||||||
|
setError={setError}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -30,7 +30,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Query types that have been migrated to React
|
// Query types that have been migrated to React
|
||||||
reactQueryEditors = [AzureQueryType.AzureMonitor];
|
reactQueryEditors = [AzureQueryType.AzureMonitor, AzureQueryType.LogAnalytics];
|
||||||
|
|
||||||
// target: AzureMonitorQuery;
|
// target: AzureMonitorQuery;
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@ import Datasource from './datasource';
|
|||||||
|
|
||||||
export type AzureDataSourceSettings = DataSourceSettings<AzureDataSourceJsonData, AzureDataSourceSecureJsonData>;
|
export type AzureDataSourceSettings = DataSourceSettings<AzureDataSourceJsonData, AzureDataSourceSecureJsonData>;
|
||||||
|
|
||||||
|
export type AzureResultFormat = 'time_series' | 'table';
|
||||||
|
|
||||||
export enum AzureQueryType {
|
export enum AzureQueryType {
|
||||||
AzureMonitor = 'Azure Monitor',
|
AzureMonitor = 'Azure Monitor',
|
||||||
ApplicationInsights = 'Application Insights',
|
ApplicationInsights = 'Application Insights',
|
||||||
|
Loading…
Reference in New Issue
Block a user