mirror of https://github.com/grafana/grafana
Cloudwatch: Refactor metrics query editor (#48537)
* refactor metrics query editor to return a function component * add tests for metrics query editor * add simple render tests for panel query editor * remove obsolete test * pr feedbackpull/48589/head
parent
39ee365b82
commit
a5c570a5f6
@ -0,0 +1,133 @@ |
|||||||
|
import { act, render, screen } from '@testing-library/react'; |
||||||
|
import React from 'react'; |
||||||
|
|
||||||
|
import { QueryEditorProps } from '@grafana/data'; |
||||||
|
|
||||||
|
import { setupMockedDataSource } from '../__mocks__/CloudWatchDataSource'; |
||||||
|
import { CloudWatchDatasource } from '../datasource'; |
||||||
|
import { CloudWatchQuery, CloudWatchJsonData, MetricEditorMode, MetricQueryType } from '../types'; |
||||||
|
|
||||||
|
import { PanelQueryEditor } from './PanelQueryEditor'; |
||||||
|
|
||||||
|
// the following three fields are added to legacy queries in the dashboard migrator
|
||||||
|
const migratedFields = { |
||||||
|
statistic: 'Average', |
||||||
|
metricEditorMode: MetricEditorMode.Builder, |
||||||
|
metricQueryType: MetricQueryType.Query, |
||||||
|
}; |
||||||
|
|
||||||
|
const props: QueryEditorProps<CloudWatchDatasource, CloudWatchQuery, CloudWatchJsonData> = { |
||||||
|
datasource: setupMockedDataSource().datasource, |
||||||
|
onRunQuery: jest.fn(), |
||||||
|
onChange: jest.fn(), |
||||||
|
query: {} as CloudWatchQuery, |
||||||
|
}; |
||||||
|
|
||||||
|
describe('PanelQueryEditor should render right editor', () => { |
||||||
|
describe('when using grafana 6.3.0 metric query', () => { |
||||||
|
it('should render the metrics query editor', async () => { |
||||||
|
const query = { |
||||||
|
...migratedFields, |
||||||
|
dimensions: { |
||||||
|
InstanceId: 'i-123', |
||||||
|
}, |
||||||
|
expression: '', |
||||||
|
highResolution: false, |
||||||
|
id: '', |
||||||
|
metricName: 'CPUUtilization', |
||||||
|
namespace: 'AWS/EC2', |
||||||
|
period: '', |
||||||
|
refId: 'A', |
||||||
|
region: 'default', |
||||||
|
returnData: false, |
||||||
|
}; |
||||||
|
await act(async () => { |
||||||
|
render(<PanelQueryEditor {...props} query={query} />); |
||||||
|
}); |
||||||
|
expect(screen.getByText('Metric name')).toBeInTheDocument(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('when using grafana 7.0.0 style metrics query', () => { |
||||||
|
it('should render the metrics query editor', async () => { |
||||||
|
const query = { |
||||||
|
...migratedFields, |
||||||
|
alias: '', |
||||||
|
apiMode: 'Logs', |
||||||
|
dimensions: { |
||||||
|
InstanceId: 'i-123', |
||||||
|
}, |
||||||
|
expression: '', |
||||||
|
id: '', |
||||||
|
logGroupNames: [], |
||||||
|
matchExact: true, |
||||||
|
metricName: 'CPUUtilization', |
||||||
|
namespace: 'AWS/EC2', |
||||||
|
period: '', |
||||||
|
queryMode: 'Logs', |
||||||
|
refId: 'A', |
||||||
|
region: 'ap-northeast-2', |
||||||
|
statistics: 'Average', |
||||||
|
} as any; |
||||||
|
await act(async () => { |
||||||
|
render(<PanelQueryEditor {...props} query={query} />); |
||||||
|
}); |
||||||
|
expect(screen.getByText('Choose Log Groups')).toBeInTheDocument(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('when using grafana 7.0.0 style logs query', () => { |
||||||
|
it('should render the metrics query editor', async () => { |
||||||
|
const query = { |
||||||
|
...migratedFields, |
||||||
|
alias: '', |
||||||
|
apiMode: 'Logs', |
||||||
|
dimensions: { |
||||||
|
InstanceId: 'i-123', |
||||||
|
}, |
||||||
|
expression: '', |
||||||
|
id: '', |
||||||
|
logGroupNames: [], |
||||||
|
matchExact: true, |
||||||
|
metricName: 'CPUUtilization', |
||||||
|
namespace: 'AWS/EC2', |
||||||
|
period: '', |
||||||
|
queryMode: 'Logs', |
||||||
|
refId: 'A', |
||||||
|
region: 'ap-northeast-2', |
||||||
|
statistic: 'Average', |
||||||
|
} as any; |
||||||
|
await act(async () => { |
||||||
|
render(<PanelQueryEditor {...props} query={query} />); |
||||||
|
}); |
||||||
|
expect(screen.getByText('Log Groups')).toBeInTheDocument(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('when using grafana query from curated ec2 dashboard', () => { |
||||||
|
it('should render the metrics query editor', async () => { |
||||||
|
const query = { |
||||||
|
...migratedFields, |
||||||
|
|
||||||
|
alias: 'Inbound', |
||||||
|
dimensions: { |
||||||
|
InstanceId: '*', |
||||||
|
}, |
||||||
|
expression: |
||||||
|
"SUM(REMOVE_EMPTY(SEARCH('{AWS/EC2,InstanceId} MetricName=\"NetworkIn\"', 'Sum', $period)))/$period", |
||||||
|
id: '', |
||||||
|
matchExact: true, |
||||||
|
metricName: 'NetworkOut', |
||||||
|
namespace: 'AWS/EC2', |
||||||
|
period: '$period', |
||||||
|
refId: 'B', |
||||||
|
region: '$region', |
||||||
|
statistic: 'Average', |
||||||
|
} as any; |
||||||
|
await act(async () => { |
||||||
|
render(<PanelQueryEditor {...props} query={query} />); |
||||||
|
}); |
||||||
|
expect(screen.getByText('Metric name')).toBeInTheDocument(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,76 @@ |
|||||||
|
import { renderHook } from '@testing-library/react-hooks'; |
||||||
|
|
||||||
|
import { CloudWatchMetricsQuery, MetricEditorMode, MetricQueryType } from '../types'; |
||||||
|
|
||||||
|
import usePreparedMetricsQuery, { DEFAULT_QUERY } from './usePreparedMetricsQuery'; |
||||||
|
|
||||||
|
interface TestScenario { |
||||||
|
name: string; |
||||||
|
query: any; |
||||||
|
expectedQuery: CloudWatchMetricsQuery; |
||||||
|
} |
||||||
|
|
||||||
|
const baseQuery: CloudWatchMetricsQuery = { |
||||||
|
refId: 'A', |
||||||
|
id: '', |
||||||
|
region: 'us-east-2', |
||||||
|
namespace: 'AWS/EC2', |
||||||
|
dimensions: { InstanceId: 'x-123' }, |
||||||
|
}; |
||||||
|
|
||||||
|
describe('usePrepareMetricsQuery', () => { |
||||||
|
describe('when an incomplete query is provided', () => { |
||||||
|
const testTable: TestScenario[] = [ |
||||||
|
{ name: 'Empty query', query: { refId: 'A' }, expectedQuery: { ...DEFAULT_QUERY, refId: 'A' } }, |
||||||
|
{ |
||||||
|
name: 'Match exact is not part of the query', |
||||||
|
query: { ...baseQuery }, |
||||||
|
expectedQuery: { ...DEFAULT_QUERY, ...baseQuery, matchExact: true }, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Match exact is part of the query', |
||||||
|
query: { ...baseQuery, matchExact: false }, |
||||||
|
expectedQuery: { ...DEFAULT_QUERY, ...baseQuery, matchExact: false }, |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'When editor mode and builder mode different from default is specified', |
||||||
|
query: { ...baseQuery, metricQueryType: MetricQueryType.Query, metricEditorMode: MetricEditorMode.Code }, |
||||||
|
expectedQuery: { |
||||||
|
...DEFAULT_QUERY, |
||||||
|
...baseQuery, |
||||||
|
metricQueryType: MetricQueryType.Query, |
||||||
|
metricEditorMode: MetricEditorMode.Code, |
||||||
|
}, |
||||||
|
}, |
||||||
|
]; |
||||||
|
describe.each(testTable)('scenario %#: $name', (scenario) => { |
||||||
|
it('should set the default values and trigger onChangeQuery', async () => { |
||||||
|
const onChangeQuery = jest.fn(); |
||||||
|
const { result } = renderHook(() => usePreparedMetricsQuery(scenario.query, onChangeQuery)); |
||||||
|
expect(onChangeQuery).toHaveBeenLastCalledWith(result.current); |
||||||
|
expect(result.current).toEqual(scenario.expectedQuery); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('when a complete query is provided', () => { |
||||||
|
it('should not change the query and should not call onChangeQuery', async () => { |
||||||
|
const onChangeQuery = jest.fn(); |
||||||
|
const completeQuery: CloudWatchMetricsQuery = { |
||||||
|
...baseQuery, |
||||||
|
expression: '', |
||||||
|
queryMode: 'Metrics', |
||||||
|
metricName: '', |
||||||
|
statistic: 'Sum', |
||||||
|
period: '300', |
||||||
|
metricQueryType: MetricQueryType.Query, |
||||||
|
metricEditorMode: MetricEditorMode.Code, |
||||||
|
sqlExpression: 'SELECT 1', |
||||||
|
matchExact: false, |
||||||
|
}; |
||||||
|
const { result } = renderHook(() => usePreparedMetricsQuery(completeQuery, onChangeQuery)); |
||||||
|
expect(onChangeQuery).not.toHaveBeenCalled(); |
||||||
|
expect(result.current).toEqual(completeQuery); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,48 @@ |
|||||||
|
import deepEqual from 'fast-deep-equal'; |
||||||
|
import { useEffect, useMemo } from 'react'; |
||||||
|
|
||||||
|
import { CloudWatchMetricsQuery, MetricEditorMode, MetricQueryType } from '../types'; |
||||||
|
|
||||||
|
export const DEFAULT_QUERY: Omit<CloudWatchMetricsQuery, 'refId'> = { |
||||||
|
queryMode: 'Metrics', |
||||||
|
namespace: '', |
||||||
|
metricName: '', |
||||||
|
expression: '', |
||||||
|
dimensions: {}, |
||||||
|
region: 'default', |
||||||
|
id: '', |
||||||
|
statistic: 'Average', |
||||||
|
period: '', |
||||||
|
metricQueryType: MetricQueryType.Search, |
||||||
|
metricEditorMode: MetricEditorMode.Builder, |
||||||
|
sqlExpression: '', |
||||||
|
matchExact: true, |
||||||
|
}; |
||||||
|
|
||||||
|
const prepareQuery = (query: CloudWatchMetricsQuery) => { |
||||||
|
const withDefaults = { ...DEFAULT_QUERY, ...query }; |
||||||
|
|
||||||
|
// If we didn't make any changes to the object, then return the original object to keep the
|
||||||
|
// identity the same, and not trigger any other useEffects or anything.
|
||||||
|
return deepEqual(withDefaults, query) ? query : withDefaults; |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns queries with some defaults + migrations, and calls onChange function to notify if it changes |
||||||
|
*/ |
||||||
|
const usePreparedMetricsQuery = ( |
||||||
|
query: CloudWatchMetricsQuery, |
||||||
|
onChangeQuery: (newQuery: CloudWatchMetricsQuery) => void |
||||||
|
) => { |
||||||
|
const preparedQuery = useMemo(() => prepareQuery(query), [query]); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (preparedQuery !== query) { |
||||||
|
onChangeQuery(preparedQuery); |
||||||
|
} |
||||||
|
}, [preparedQuery, query, onChangeQuery]); |
||||||
|
|
||||||
|
return preparedQuery; |
||||||
|
}; |
||||||
|
|
||||||
|
export default usePreparedMetricsQuery; |
Loading…
Reference in new issue