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