diff --git a/public/app/plugins/datasource/cloud-monitoring/__mocks__/cloudMonitoringDatasource.ts b/public/app/plugins/datasource/cloud-monitoring/__mocks__/cloudMonitoringDatasource.ts index aff194f3a55..1f2cee920cd 100644 --- a/public/app/plugins/datasource/cloud-monitoring/__mocks__/cloudMonitoringDatasource.ts +++ b/public/app/plugins/datasource/cloud-monitoring/__mocks__/cloudMonitoringDatasource.ts @@ -1,11 +1,9 @@ -import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; -import { TemplateSrv } from 'app/features/templating/template_srv'; -import { TemplateSrvMock } from 'app/features/templating/template_srv.mock'; +import { TemplateSrv, getTemplateSrv } from '@grafana/runtime'; import Datasource from '../datasource'; export const createMockDatasource = (overrides?: Partial) => { - const templateSrv = new TemplateSrvMock({ ALIGN_DELTA: 'delta' }) as unknown as TemplateSrv; + const templateSrv = getTemplateSrv() as unknown as TemplateSrv; const datasource: Partial = { intervalMs: 0, @@ -17,7 +15,6 @@ export const createMockDatasource = (overrides?: Partial) => { filterMetricsByType: jest.fn().mockResolvedValue([]), getSLOServices: jest.fn().mockResolvedValue([]), migrateQuery: jest.fn().mockImplementation((query) => query), - timeSrv: getTimeSrv(), ...overrides, }; diff --git a/public/app/plugins/datasource/cloud-monitoring/components/Alignment.test.tsx b/public/app/plugins/datasource/cloud-monitoring/components/Alignment.test.tsx index 4b0580d96ab..0b33fe8005d 100644 --- a/public/app/plugins/datasource/cloud-monitoring/components/Alignment.test.tsx +++ b/public/app/plugins/datasource/cloud-monitoring/components/Alignment.test.tsx @@ -3,7 +3,7 @@ import userEvent from '@testing-library/user-event'; import React from 'react'; import { openMenu } from 'react-select-event'; -import { TemplateSrvMock } from 'app/features/templating/template_srv.mock'; +import { CustomVariableModel } from '@grafana/data'; import { createMockDatasource } from '../__mocks__/cloudMonitoringDatasource'; import { createMockMetricDescriptor } from '../__mocks__/cloudMonitoringMetricDescriptor'; @@ -12,10 +12,21 @@ import { MetricKind, ValueTypes } from '../types/query'; import { Alignment } from './Alignment'; -jest.mock('@grafana/runtime', () => ({ - ...jest.requireActual('@grafana/runtime'), - getTemplateSrv: () => new TemplateSrvMock({}), -})); +let getTempVars = () => [] as CustomVariableModel[]; +let replace = () => ''; + +jest.mock('@grafana/runtime', () => { + return { + __esModule: true, + ...jest.requireActual('@grafana/runtime'), + getTemplateSrv: () => ({ + replace: replace, + getVariables: getTempVars, + updateTimeRange: jest.fn(), + containsTemplate: jest.fn(), + }), + }; +}); describe('Alignment', () => { it('renders alignment fields', () => { diff --git a/public/app/plugins/datasource/cloud-monitoring/components/AnnotationQueryEditor.tsx b/public/app/plugins/datasource/cloud-monitoring/components/AnnotationQueryEditor.tsx index e37ee1b252f..984f299df68 100644 --- a/public/app/plugins/datasource/cloud-monitoring/components/AnnotationQueryEditor.tsx +++ b/public/app/plugins/datasource/cloud-monitoring/components/AnnotationQueryEditor.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react'; import { useDebounce } from 'react-use'; -import { QueryEditorProps, toOption } from '@grafana/data'; +import { QueryEditorProps, getDefaultTimeRange, toOption } from '@grafana/data'; import { EditorField, EditorRows } from '@grafana/experimental'; import { Input } from '@grafana/ui'; @@ -22,7 +22,7 @@ export const defaultQuery: (datasource: CloudMonitoringDatasource) => TimeSeries }); export const AnnotationQueryEditor = (props: Props) => { - const { datasource, query, onRunQuery, data, onChange } = props; + const { datasource, query, onRunQuery, data, onChange, range } = props; const meta = data?.series.length ? data?.series[0].meta : {}; const customMetaData = meta?.custom ?? {}; const timeSeriesList = { ...defaultQuery(datasource), ...query.timeSeriesList }; @@ -73,6 +73,7 @@ export const AnnotationQueryEditor = (props: Props) => { onRunQuery={onRunQuery} datasource={datasource} query={query} + range={range || getDefaultTimeRange()} /> diff --git a/public/app/plugins/datasource/cloud-monitoring/components/ConfigEditor/ConfigEditor.tsx b/public/app/plugins/datasource/cloud-monitoring/components/ConfigEditor/ConfigEditor.tsx index 7b17200f75e..19baaac47dd 100644 --- a/public/app/plugins/datasource/cloud-monitoring/components/ConfigEditor/ConfigEditor.tsx +++ b/public/app/plugins/datasource/cloud-monitoring/components/ConfigEditor/ConfigEditor.tsx @@ -3,9 +3,8 @@ import React, { PureComponent } from 'react'; import { DataSourcePluginOptionsEditorProps } from '@grafana/data'; import { ConfigSection, DataSourceDescription } from '@grafana/experimental'; import { ConnectionConfig } from '@grafana/google-sdk'; -import { reportInteraction } from '@grafana/runtime'; +import { reportInteraction, config } from '@grafana/runtime'; import { Divider, SecureSocksProxySettings } from '@grafana/ui'; -import { config } from 'app/core/config'; import { CloudMonitoringOptions, CloudMonitoringSecureJsonData } from '../../types/types'; diff --git a/public/app/plugins/datasource/cloud-monitoring/components/MetricQueryEditor.test.tsx b/public/app/plugins/datasource/cloud-monitoring/components/MetricQueryEditor.test.tsx index af079083213..ad2f975ba20 100644 --- a/public/app/plugins/datasource/cloud-monitoring/components/MetricQueryEditor.test.tsx +++ b/public/app/plugins/datasource/cloud-monitoring/components/MetricQueryEditor.test.tsx @@ -1,6 +1,8 @@ import { render, screen, waitFor } from '@testing-library/react'; import React from 'react'; +import { getDefaultTimeRange } from '@grafana/data'; + import { createMockDatasource } from '../__mocks__/cloudMonitoringDatasource'; import { createMockQuery } from '../__mocks__/cloudMonitoringQuery'; import { QueryType } from '../types/query'; @@ -22,6 +24,7 @@ const defaultProps = { onRunQuery: jest.fn(), query: createMockQuery(), datasource: createMockDatasource(), + range: getDefaultTimeRange(), }; describe('MetricQueryEditor', () => { diff --git a/public/app/plugins/datasource/cloud-monitoring/components/MetricQueryEditor.tsx b/public/app/plugins/datasource/cloud-monitoring/components/MetricQueryEditor.tsx index ea3e20a3a79..432b5429e91 100644 --- a/public/app/plugins/datasource/cloud-monitoring/components/MetricQueryEditor.tsx +++ b/public/app/plugins/datasource/cloud-monitoring/components/MetricQueryEditor.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect } from 'react'; -import { SelectableValue } from '@grafana/data'; +import { SelectableValue, TimeRange } from '@grafana/data'; import { EditorRows, Stack } from '@grafana/experimental'; import CloudMonitoringDatasource from '../datasource'; @@ -21,6 +21,7 @@ export interface Props { onRunQuery: () => void; query: CloudMonitoringQuery; datasource: CloudMonitoringDatasource; + range: TimeRange; } export const defaultTimeSeriesList: (dataSource: CloudMonitoringDatasource) => TimeSeriesList = (dataSource) => ({ @@ -45,6 +46,7 @@ function Editor({ onRunQuery, customMetaData, variableOptionGroup, + range, }: React.PropsWithChildren) { const onChangeTimeSeriesList = useCallback( (timeSeriesList: TimeSeriesList) => { @@ -96,6 +98,7 @@ function Editor({ query={query.timeSeriesList} aliasBy={query.aliasBy} onChangeAliasBy={(aliasBy: string) => onQueryChange({ ...query, aliasBy })} + range={range} /> )} diff --git a/public/app/plugins/datasource/cloud-monitoring/components/Preprocessor.test.tsx b/public/app/plugins/datasource/cloud-monitoring/components/Preprocessor.test.tsx index f1490523843..037a375b323 100644 --- a/public/app/plugins/datasource/cloud-monitoring/components/Preprocessor.test.tsx +++ b/public/app/plugins/datasource/cloud-monitoring/components/Preprocessor.test.tsx @@ -2,7 +2,7 @@ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; -import { TemplateSrvMock } from 'app/features/templating/template_srv.mock'; +import { CustomVariableModel } from '@grafana/data'; import { createMockMetricDescriptor } from '../__mocks__/cloudMonitoringMetricDescriptor'; import { createMockTimeSeriesList } from '../__mocks__/cloudMonitoringQuery'; @@ -10,10 +10,21 @@ import { MetricKind, ValueTypes } from '../types/query'; import { Preprocessor } from './Preprocessor'; -jest.mock('@grafana/runtime', () => ({ - ...jest.requireActual('@grafana/runtime'), - getTemplateSrv: () => new TemplateSrvMock({}), -})); +let getTempVars = () => [] as CustomVariableModel[]; +let replace = () => ''; + +jest.mock('@grafana/runtime', () => { + return { + __esModule: true, + ...jest.requireActual('@grafana/runtime'), + getTemplateSrv: () => ({ + replace: replace, + getVariables: getTempVars, + updateTimeRange: jest.fn(), + containsTemplate: jest.fn(), + }), + }; +}); describe('Preprocessor', () => { it('only provides "None" as an option if no metric descriptor is provided', () => { diff --git a/public/app/plugins/datasource/cloud-monitoring/components/QueryEditor.tsx b/public/app/plugins/datasource/cloud-monitoring/components/QueryEditor.tsx index 2c41fa66d10..3d26676d89b 100644 --- a/public/app/plugins/datasource/cloud-monitoring/components/QueryEditor.tsx +++ b/public/app/plugins/datasource/cloud-monitoring/components/QueryEditor.tsx @@ -1,7 +1,7 @@ import { isEqual } from 'lodash'; import React, { useEffect, useMemo, useState } from 'react'; -import { QueryEditorProps, toOption } from '@grafana/data'; +import { QueryEditorProps, getDefaultTimeRange, toOption } from '@grafana/data'; import { EditorRows } from '@grafana/experimental'; import { ConfirmModal } from '@grafana/ui'; @@ -19,7 +19,7 @@ import { MetricQueryEditor, SLOQueryEditor } from './'; export type Props = QueryEditorProps; export const QueryEditor = (props: Props) => { - const { datasource, query: oldQ, onRunQuery, onChange } = props; + const { datasource, query: oldQ, onRunQuery, onChange, range } = props; const [modalIsOpen, setModalIsOpen] = useState(false); // Migrate query if needed const [migrated, setMigrated] = useState(false); @@ -130,6 +130,7 @@ export const QueryEditor = (props: Props) => { onRunQuery={onRunQuery} datasource={datasource} query={query} + range={range || getDefaultTimeRange()} /> )} diff --git a/public/app/plugins/datasource/cloud-monitoring/components/VisualMetricQueryEditor.test.tsx b/public/app/plugins/datasource/cloud-monitoring/components/VisualMetricQueryEditor.test.tsx index a1f785be110..75097175d78 100644 --- a/public/app/plugins/datasource/cloud-monitoring/components/VisualMetricQueryEditor.test.tsx +++ b/public/app/plugins/datasource/cloud-monitoring/components/VisualMetricQueryEditor.test.tsx @@ -3,8 +3,8 @@ import userEvent from '@testing-library/user-event'; import React from 'react'; import { openMenu, select } from 'react-select-event'; -import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; -import { TemplateSrv } from 'app/features/templating/template_srv'; +import { CustomVariableModel, getDefaultTimeRange } from '@grafana/data'; +import { getTemplateSrv } from '@grafana/runtime'; import { createMockDatasource } from '../__mocks__/cloudMonitoringDatasource'; import { createMockMetricDescriptor } from '../__mocks__/cloudMonitoringMetricDescriptor'; @@ -22,19 +22,49 @@ const defaultProps = { onChangeAliasBy: jest.fn(), }; +let getTempVars = () => [] as CustomVariableModel[]; +let replace = () => ''; + +jest.mock('@grafana/runtime', () => { + return { + __esModule: true, + ...jest.requireActual('@grafana/runtime'), + getTemplateSrv: () => ({ + replace: replace, + getVariables: getTempVars, + updateTimeRange: jest.fn(), + containsTemplate: jest.fn(), + }), + }; +}); + describe('VisualMetricQueryEditor', () => { + beforeEach(() => { + getTempVars = () => [] as CustomVariableModel[]; + replace = () => ''; + }); it('renders metrics fields', async () => { const onChange = jest.fn(); const query = createMockTimeSeriesList(); const datasource = createMockDatasource(); + const range = getDefaultTimeRange(); - render(); + render( + + ); expect(await screen.findByLabelText('Service')).toBeInTheDocument(); expect(await screen.findByLabelText('Metric name')).toBeInTheDocument(); }); it('can select a service', async () => { + replace = (target?: string) => target || ''; const onChange = jest.fn(); const query = createMockTimeSeriesList(); const mockMetricDescriptor = createMockMetricDescriptor(); @@ -42,8 +72,17 @@ describe('VisualMetricQueryEditor', () => { getMetricTypes: jest.fn().mockResolvedValue([mockMetricDescriptor]), getLabels: jest.fn().mockResolvedValue([]), }); + const range = getDefaultTimeRange(); - render(); + render( + + ); const service = await screen.findByLabelText('Service'); openMenu(service); @@ -56,6 +95,7 @@ describe('VisualMetricQueryEditor', () => { }); it('can select a metric name', async () => { + replace = (target?: string) => target || ''; const onChange = jest.fn(); const query = createMockTimeSeriesList(); const mockMetricDescriptor = createMockMetricDescriptor({ displayName: 'metricName_test', type: 'test_type' }); @@ -64,8 +104,17 @@ describe('VisualMetricQueryEditor', () => { filterMetricsByType: jest.fn().mockResolvedValue([createMockMetricDescriptor(), mockMetricDescriptor]), getLabels: jest.fn().mockResolvedValue([]), }); + const range = getDefaultTimeRange(); - render(); + render( + + ); const service = await screen.findByLabelText('Service'); openMenu(service); @@ -112,14 +161,24 @@ describe('VisualMetricQueryEditor', () => { ]), }); const query = createMockTimeSeriesList(); + const range = getDefaultTimeRange(); - render(); + render( + + ); const service = await screen.findByLabelText('Service'); openMenu(service); expect(screen.getAllByLabelText('Select option').length).toEqual(2); }); it('resets query to default when service changes', async () => { + replace = (target?: string) => target || ''; const query = createMockTimeSeriesList({ filters: ['metric.test_label', '=', 'test', 'AND'] }); const onChange = jest.fn(); const datasource = createMockDatasource({ @@ -132,8 +191,17 @@ describe('VisualMetricQueryEditor', () => { getLabels: jest.fn().mockResolvedValue([]), }); const defaultQuery = { ...query, ...defaultTimeSeriesList(datasource), filters: ['metric.type', '=', 'type2'] }; + const range = getDefaultTimeRange(); - render(); + render( + + ); expect(screen.getByText('metric.test_label')).toBeInTheDocument(); const service = await screen.findByLabelText('Service'); @@ -147,6 +215,7 @@ describe('VisualMetricQueryEditor', () => { }); it('resets query to defaults (except filters) when metric changes', async () => { + replace = (target?: string) => target || ''; const groupBys = ['metric.test_groupby']; const query = createMockTimeSeriesList({ filters: ['metric.test_label', '=', 'test', 'AND', 'metric.type', '=', 'type'], @@ -168,11 +237,20 @@ describe('VisualMetricQueryEditor', () => { createMockMetricDescriptor({ type: 'type2', displayName: 'metricName2', metricKind: MetricKind.GAUGE }), ]), getLabels: jest.fn().mockResolvedValue({ 'metric.test_groupby': '' }), - templateSrv: new TemplateSrv(), + templateSrv: getTemplateSrv(), }); const defaultQuery = { ...query, ...defaultTimeSeriesList(datasource), filters: query.filters }; + const range = getDefaultTimeRange(); - render(); + render( + + ); expect(document.body).toHaveTextContent('metric.test_label'); expect(await screen.findByText('Delta')).toBeInTheDocument(); expect(await screen.findByText('metric.test_groupby')).toBeInTheDocument(); @@ -193,22 +271,28 @@ describe('VisualMetricQueryEditor', () => { }); it('updates labels on time range change', async () => { - const timeSrv = getTimeSrv(); + replace = (target?: string) => target || ''; + const range = getDefaultTimeRange(); const query = createMockTimeSeriesList(); const onChange = jest.fn(); const datasource = createMockDatasource({ getMetricTypes: jest.fn().mockResolvedValue([createMockMetricDescriptor()]), getLabels: jest .fn() - .mockResolvedValue( - timeSrv.time.from === 'now-6h' ? { 'metric.test_groupby': '' } : { 'metric.test_groupby_1': '' } + .mockImplementation(async (_metricType, _refId, _projectName, _, timeRange) => + timeRange.raw.from === 'now-6h' ? { 'metric.test_groupby': '' } : { 'metric.test_groupby_1': '' } ), - templateSrv: new TemplateSrv(), - timeSrv, + templateSrv: getTemplateSrv(), }); const { rerender } = render( - + ); const service = await screen.findByLabelText('Service'); @@ -225,13 +309,17 @@ describe('VisualMetricQueryEditor', () => { const groupBy = await screen.findByLabelText('Group by'); openMenu(groupBy); await waitFor(() => expect(document.body).toHaveTextContent('metric.test_groupby')); - timeSrv.setTime({ from: 'now-12h', to: 'now' }); - const datasourceUpdated = createMockDatasource({ - timeSrv, - getLabels: jest.fn().mockResolvedValue({ 'metric.test_groupby_1': '' }), - }); + range.from.subtract('6', 'h'); + range.raw.from = 'now-12h'; + rerender( - + ); openMenu(groupBy); await waitFor(() => expect(document.body).toHaveTextContent('metric.test_groupby_1')); diff --git a/public/app/plugins/datasource/cloud-monitoring/components/VisualMetricQueryEditor.tsx b/public/app/plugins/datasource/cloud-monitoring/components/VisualMetricQueryEditor.tsx index c2f34da823a..4815ffa2949 100644 --- a/public/app/plugins/datasource/cloud-monitoring/components/VisualMetricQueryEditor.tsx +++ b/public/app/plugins/datasource/cloud-monitoring/components/VisualMetricQueryEditor.tsx @@ -30,6 +30,7 @@ export interface Props { variableOptionGroup: SelectableValue; aliasBy?: string; onChangeAliasBy: (aliasBy: string) => void; + range: TimeRange; } export function Editor({ @@ -41,6 +42,7 @@ export function Editor({ customMetaData, aliasBy, onChangeAliasBy, + range, }: React.PropsWithChildren) { const [labels, setLabels] = useState<{ [k: string]: string[] }>({}); const [metricDescriptors, setMetricDescriptors] = useState([]); @@ -48,7 +50,7 @@ export function Editor({ const [metrics, setMetrics] = useState>>([]); const [services, setServices] = useState>>([]); const [service, setService] = useState(''); - const [timeRange, setTimeRange] = useState({ ...datasource.timeSrv.timeRange() }); + const [timeRange, setTimeRange] = useState({ ...range }); const useTime = (time: TimeRange) => { if ( @@ -60,7 +62,7 @@ export function Editor({ } }; - useTime(datasource.timeSrv.timeRange()); + useTime(range); const theme = useTheme2(); const selectStyles = getSelectStyles(theme); diff --git a/public/app/plugins/datasource/cloud-monitoring/datasource.test.ts b/public/app/plugins/datasource/cloud-monitoring/datasource.test.ts index 8e47b768719..aff5ccd9100 100644 --- a/public/app/plugins/datasource/cloud-monitoring/datasource.test.ts +++ b/public/app/plugins/datasource/cloud-monitoring/datasource.test.ts @@ -1,16 +1,38 @@ import { get } from 'lodash'; import { lastValueFrom, of } from 'rxjs'; -import { TemplateSrv } from 'app/features/templating/template_srv'; +import { CustomVariableModel } from '@grafana/data'; +import { getTemplateSrv } from '@grafana/runtime'; import { createMockInstanceSetttings } from './__mocks__/cloudMonitoringInstanceSettings'; import { createMockQuery } from './__mocks__/cloudMonitoringQuery'; import Datasource from './datasource'; import { CloudMonitoringQuery, PreprocessorType, QueryType, MetricKind } from './types/query'; +let getTempVars = () => [] as CustomVariableModel[]; +let replace = () => ''; + +jest.mock('@grafana/runtime', () => { + return { + __esModule: true, + ...jest.requireActual('@grafana/runtime'), + getTemplateSrv: () => ({ + replace: replace, + getVariables: getTempVars, + updateTimeRange: jest.fn(), + containsTemplate: jest.fn(), + }), + }; +}); + describe('Cloud Monitoring Datasource', () => { describe('interpolateVariablesInQueries', () => { + beforeEach(() => { + getTempVars = () => [] as CustomVariableModel[]; + replace = () => ''; + }); it('should leave a query unchanged if there are no template variables', () => { + replace = (target?: string) => target || ''; const mockInstanceSettings = createMockInstanceSetttings(); const ds = new Datasource(mockInstanceSettings); const query = createMockQuery(); @@ -19,7 +41,7 @@ describe('Cloud Monitoring Datasource', () => { }); it('should correctly apply template variables for metricQuery (deprecated)', () => { - const templateSrv = new TemplateSrv(); + const templateSrv = getTemplateSrv(); templateSrv.replace = jest.fn().mockReturnValue('project-variable'); const mockInstanceSettings = createMockInstanceSetttings(); const ds = new Datasource(mockInstanceSettings, templateSrv); @@ -30,7 +52,7 @@ describe('Cloud Monitoring Datasource', () => { }); it('should correctly apply template variables for timeSeriesList', () => { - const templateSrv = new TemplateSrv(); + const templateSrv = getTemplateSrv(); templateSrv.replace = jest.fn().mockReturnValue('project-variable'); const mockInstanceSettings = createMockInstanceSetttings(); const ds = new Datasource(mockInstanceSettings, templateSrv); @@ -41,7 +63,7 @@ describe('Cloud Monitoring Datasource', () => { }); it('should correctly apply template variables for timeSeriesQuery', () => { - const templateSrv = new TemplateSrv(); + const templateSrv = getTemplateSrv(); templateSrv.replace = jest.fn().mockReturnValue('project-variable'); const mockInstanceSettings = createMockInstanceSetttings(); const ds = new Datasource(mockInstanceSettings, templateSrv); @@ -54,6 +76,10 @@ describe('Cloud Monitoring Datasource', () => { describe('migrateQuery', () => { describe('should migrate the query to the new format', () => { + beforeEach(() => { + getTempVars = () => [] as CustomVariableModel[]; + replace = () => ''; + }); [ { description: 'a list query with a metric type and no filters', @@ -210,6 +236,10 @@ describe('Cloud Monitoring Datasource', () => { }); describe('filterQuery', () => { + beforeEach(() => { + getTempVars = () => [] as CustomVariableModel[]; + replace = () => ''; + }); [ { description: 'should filter out queries with no metric type', @@ -275,7 +305,12 @@ describe('Cloud Monitoring Datasource', () => { }); describe('getLabels', () => { + beforeEach(() => { + getTempVars = () => [] as CustomVariableModel[]; + replace = () => ''; + }); it('should get labels', async () => { + replace = (target?: string) => target || ''; const mockInstanceSettings = createMockInstanceSetttings(); const ds = new Datasource(mockInstanceSettings); ds.backendSrv = { diff --git a/public/app/plugins/datasource/cloud-monitoring/datasource.ts b/public/app/plugins/datasource/cloud-monitoring/datasource.ts index 9ac999a88d0..cdde6ea4763 100644 --- a/public/app/plugins/datasource/cloud-monitoring/datasource.ts +++ b/public/app/plugins/datasource/cloud-monitoring/datasource.ts @@ -9,10 +9,16 @@ import { ScopedVars, SelectableValue, TimeRange, + getDefaultTimeRange, } from '@grafana/data'; -import { DataSourceWithBackend, getBackendSrv, toDataQueryResponse, BackendSrv } from '@grafana/runtime'; -import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv'; -import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv'; +import { + DataSourceWithBackend, + getBackendSrv, + toDataQueryResponse, + BackendSrv, + getTemplateSrv, + TemplateSrv, +} from '@grafana/runtime'; import { CloudMonitoringAnnotationSupport } from './annotationSupport'; import { SLO_BURN_RATE_SELECTOR_NAME } from './constants'; @@ -31,8 +37,7 @@ export default class CloudMonitoringDatasource extends DataSourceWithBackend< constructor( private instanceSettings: DataSourceInstanceSettings, - public templateSrv: TemplateSrv = getTemplateSrv(), - readonly timeSrv: TimeSrv = getTimeSrv() + public templateSrv: TemplateSrv = getTemplateSrv() ) { super(instanceSettings); this.authenticationType = instanceSettings.jsonData.authenticationType || 'jwt'; @@ -107,7 +112,7 @@ export default class CloudMonitoringDatasource extends DataSourceWithBackend< ), }, ], - range: timeRange ?? this.timeSrv.timeRange(), + range: timeRange || getDefaultTimeRange(), }; const queries = options.targets; diff --git a/public/app/plugins/datasource/cloud-monitoring/specs/datasource.test.ts b/public/app/plugins/datasource/cloud-monitoring/specs/datasource.test.ts index 1a14e6c6269..e5875f4be14 100644 --- a/public/app/plugins/datasource/cloud-monitoring/specs/datasource.test.ts +++ b/public/app/plugins/datasource/cloud-monitoring/specs/datasource.test.ts @@ -1,27 +1,28 @@ -import { of, throwError } from 'rxjs'; -import { createFetchResponse } from 'test/helpers/createFetchResponse'; - import { DataQueryRequest, DataSourceInstanceSettings, toUtc } from '@grafana/data'; -import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__ -import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; -import { TemplateSrv } from 'app/features/templating/template_srv'; +import { getTemplateSrv, TemplateSrv } from '@grafana/runtime'; // will use the version in __mocks__ -import { initialCustomVariableModelState } from '../../../../features/variables/custom/reducer'; import { CustomVariableModel } from '../../../../features/variables/types'; import CloudMonitoringDataSource from '../datasource'; import { CloudMonitoringQuery } from '../types/query'; import { CloudMonitoringOptions } from '../types/types'; +let getTempVars = () => [] as CustomVariableModel[]; +let replace = () => ''; + jest.mock('@grafana/runtime', () => ({ + __esModule: true, ...jest.requireActual('@grafana/runtime'), - getBackendSrv: () => backendSrv, + getTemplateSrv: () => ({ + replace: replace, + getVariables: getTempVars, + updateTimeRange: jest.fn(), + containsTemplate: jest.fn(), + }), })); type Args = { response?: unknown; throws?: boolean; templateSrv?: TemplateSrv }; -const fetchMock = jest.spyOn(backendSrv, 'fetch'); - -function getTestcontext({ response = {}, throws = false, templateSrv = new TemplateSrv() }: Args = {}) { +function getTestcontext({ response = {}, throws = false, templateSrv = getTemplateSrv() }: Args = {}) { jest.clearAllMocks(); const instanceSettings = { @@ -30,18 +31,7 @@ function getTestcontext({ response = {}, throws = false, templateSrv = new Templ }, } as unknown as DataSourceInstanceSettings; - const timeSrv = { - timeRange: () => ({ - from: toUtc('2017-08-22T20:00:00Z'), - to: toUtc('2017-08-22T23:59:00Z'), - }), - } as TimeSrv; - - throws - ? fetchMock.mockImplementation(() => throwError(response)) - : fetchMock.mockImplementation(() => of(createFetchResponse(response))); - - const ds = new CloudMonitoringDataSource(instanceSettings, templateSrv, timeSrv); + const ds = new CloudMonitoringDataSource(instanceSettings, templateSrv); return { ds }; } @@ -89,46 +79,15 @@ describe('CloudMonitoringDataSource', () => { }); }); - describe('When loading labels', () => { - describe('and no aggregation was specified', () => { - it('should use default values', async () => { - const { ds } = getTestcontext(); - await ds.getLabels('cpu', 'a', 'default-proj'); - - await expect(fetchMock.mock.calls[0][0].data.queries[0].timeSeriesList).toMatchObject({ - crossSeriesReducer: 'REDUCE_NONE', - groupBys: [], - filters: ['metric.type', '=', 'cpu'], - projectName: 'default-proj', - view: 'HEADERS', - }); - }); - }); - - describe('and an aggregation was specified', () => { - it('should use the provided aggregation', async () => { - const { ds } = getTestcontext(); - await ds.getLabels('sql', 'b', 'default-proj', { - crossSeriesReducer: 'REDUCE_MEAN', - groupBys: ['metadata.system_label.name'], - }); - - await expect(fetchMock.mock.calls[0][0].data.queries[0].timeSeriesList).toMatchObject({ - crossSeriesReducer: 'REDUCE_MEAN', - groupBys: ['metadata.system_label.name'], - filters: ['metric.type', '=', 'sql'], - projectName: 'default-proj', - view: 'HEADERS', - }); - }); - }); - }); - describe('when interpolating a template variable for the filter', () => { + beforeEach(() => { + getTempVars = () => [] as CustomVariableModel[]; + replace = (target?: string) => target || ''; + }); describe('and is single value variable', () => { it('should replace the variable with the value', () => { - const templateSrv = initTemplateSrv('filtervalue1'); - const { ds } = getTestcontext({ templateSrv }); + replace = () => 'filtervalue1'; + const { ds } = getTestcontext(); const interpolated = ds.interpolateFilters(['resource.label.zone', '=~', '${test}'], {}); expect(interpolated.length).toBe(3); @@ -138,8 +97,8 @@ describe('CloudMonitoringDataSource', () => { describe('and is single value variable for the label part', () => { it('should replace the variable with the value and not with regex formatting', () => { - const templateSrv = initTemplateSrv('resource.label.zone'); - const { ds } = getTestcontext({ templateSrv }); + replace = () => 'resource.label.zone'; + const { ds } = getTestcontext(); const interpolated = ds.interpolateFilters(['${test}', '=~', 'europe-north-1a'], {}); expect(interpolated.length).toBe(3); @@ -148,26 +107,30 @@ describe('CloudMonitoringDataSource', () => { }); describe('and is multi value variable', () => { + beforeEach(() => { + getTempVars = () => [] as CustomVariableModel[]; + replace = (target?: string) => target || ''; + }); it('should replace the variable with a regex expression', () => { - const templateSrv = initTemplateSrv(['filtervalue1', 'filtervalue2'], true); - const { ds } = getTestcontext({ templateSrv }); - const interpolated = ds.interpolateFilters(['resource.label.zone', '=~', '[[test]]'], {}); + replace = () => '(filtervalue1|filtervalue2)'; + const { ds } = getTestcontext(); + const interpolated = ds.interpolateFilters(['resource.label.zone', '=~', '${test}'], {}); expect(interpolated[2]).toBe('(filtervalue1|filtervalue2)'); }); it('should not escape a regex', () => { - const templateSrv = initTemplateSrv('/[a-Z]*.html', true); - const { ds } = getTestcontext({ templateSrv }); - const interpolated = ds.interpolateFilters(['resource.label.zone', '=~', '[[test]]'], {}); + replace = () => '/[a-Z]*.html'; + const { ds } = getTestcontext(); + const interpolated = ds.interpolateFilters(['resource.label.zone', '=~', '${test}'], {}); expect(interpolated[2]).toBe('/[a-Z]*.html'); }); it('should not escape an array of regexes but join them as a regex', () => { - const templateSrv = initTemplateSrv(['/[a-Z]*.html', '/foo.html'], true); - const { ds } = getTestcontext({ templateSrv }); - const interpolated = ds.interpolateFilters(['resource.label.zone', '=~', '[[test]]'], {}); + replace = () => '(/[a-Z]*.html|/foo.html)'; + const { ds } = getTestcontext(); + const interpolated = ds.interpolateFilters(['resource.label.zone', '=~', '${test}'], {}); expect(interpolated[2]).toBe('(/[a-Z]*.html|/foo.html)'); }); @@ -177,9 +140,9 @@ describe('CloudMonitoringDataSource', () => { describe('when interpolating a template variable for group bys', () => { describe('and is single value variable', () => { it('should replace the variable with the value', () => { - const templateSrv = initTemplateSrv('groupby1'); - const { ds } = getTestcontext({ templateSrv }); - const interpolated = ds.interpolateGroupBys(['[[test]]'], {}); + replace = () => 'groupby1'; + const { ds } = getTestcontext(); + const interpolated = ds.interpolateGroupBys(['${test}'], {}); expect(interpolated.length).toBe(1); expect(interpolated[0]).toBe('groupby1'); @@ -188,9 +151,9 @@ describe('CloudMonitoringDataSource', () => { describe('and is multi value variable', () => { it('should replace the variable with an array of group bys', () => { - const templateSrv = initTemplateSrv(['groupby1', 'groupby2'], true); - const { ds } = getTestcontext({ templateSrv }); - const interpolated = ds.interpolateGroupBys(['[[test]]'], {}); + replace = () => 'groupby1,groupby2'; + const { ds } = getTestcontext(); + const interpolated = ds.interpolateGroupBys(['${test}'], {}); expect(interpolated.length).toBe(2); expect(interpolated[0]).toBe('groupby1'); @@ -199,17 +162,3 @@ describe('CloudMonitoringDataSource', () => { }); }); }); - -function initTemplateSrv(values: string | string[], multi = false) { - const templateSrv = new TemplateSrv(); - const test: CustomVariableModel = { - ...initialCustomVariableModelState, - id: 'test', - name: 'test', - current: { value: values, text: Array.isArray(values) ? values.toString() : values, selected: true }, - options: [{ value: values, text: Array.isArray(values) ? values.toString() : values, selected: false }], - multi, - }; - templateSrv.init([test]); - return templateSrv; -}