diff --git a/packages/grafana-ui/src/types/datasource.ts b/packages/grafana-ui/src/types/datasource.ts index 5448121a8d2..3b47265de73 100644 --- a/packages/grafana-ui/src/types/datasource.ts +++ b/packages/grafana-ui/src/types/datasource.ts @@ -117,15 +117,30 @@ export interface DataSourceConstructor< /** * The main data source abstraction interface, represents an instance of a data source */ -export interface DataSourceApi< +export abstract class DataSourceApi< TQuery extends DataQuery = DataQuery, TOptions extends DataSourceJsonData = DataSourceJsonData > { + /** + * Set in constructor + */ + readonly name: string; + + /** + * Set in constructor + */ + readonly id: number; + /** * min interval range */ interval?: string; + constructor(instanceSettings: DataSourceInstanceSettings) { + this.name = instanceSettings.name; + this.id = instanceSettings.id; + } + /** * Imports queries from a different datasource */ @@ -139,12 +154,12 @@ export interface DataSourceApi< /** * Main metrics / data query action */ - query(options: DataQueryRequest, observer?: DataStreamObserver): Promise; + abstract query(options: DataQueryRequest, observer?: DataStreamObserver): Promise; /** * Test & verify datasource settings & connection details */ - testDatasource(): Promise; + abstract testDatasource(): Promise; /** * Get hints for query improvements @@ -156,16 +171,6 @@ export interface DataSourceApi< */ getQueryDisplayText?(query: TQuery): string; - /** - * Set after constructor is called by Grafana - */ - name?: string; - - /** - * Set after constructor is called by Grafana - */ - id?: number; - /** * Set after constructor call, as the data source instance is the most common thing to pass around * we attach the components to this instance for easy access @@ -178,7 +183,7 @@ export interface DataSourceApi< meta?: DataSourcePluginMeta; } -export interface ExploreDataSourceApi< +export abstract class ExploreDataSourceApi< TQuery extends DataQuery = DataQuery, TOptions extends DataSourceJsonData = DataSourceJsonData > extends DataSourceApi { diff --git a/packages/grafana-ui/src/utils/moment_wrapper.ts b/packages/grafana-ui/src/utils/moment_wrapper.ts index 62b1e5b5c6e..aaf2113f799 100644 --- a/packages/grafana-ui/src/utils/moment_wrapper.ts +++ b/packages/grafana-ui/src/utils/moment_wrapper.ts @@ -59,6 +59,7 @@ export interface DateTime extends Object { subtract: (amount?: DateTimeInput, unit?: DurationUnit) => DateTime; toDate: () => Date; toISOString: () => string; + diff: (amount: DateTimeInput, unit?: DurationUnit, truncate?: boolean) => number; valueOf: () => number; unix: () => number; utc: () => DateTime; diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 2f39961fdc8..e4fb0ed34c0 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -3,6 +3,7 @@ package api import ( "strconv" + "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/bus" @@ -86,12 +87,13 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *m.ReqContext) (map[string]interf defaultDatasource = ds.Name } - if ds.JsonData != nil { - dsMap["jsonData"] = ds.JsonData - } else { - dsMap["jsonData"] = make(map[string]string) + jsonData := ds.JsonData + if jsonData == nil { + jsonData = simplejson.New() } + dsMap["jsonData"] = jsonData + if ds.Access == m.DS_ACCESS_DIRECT { if ds.BasicAuth { dsMap["basicAuth"] = util.GetBasicAuthHeader(ds.BasicAuthUser, ds.DecryptedBasicAuthPassword()) @@ -123,7 +125,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *m.ReqContext) (map[string]interf if ds.Type == m.DS_PROMETHEUS { // add unproxied server URL for link to Prometheus web UI - dsMap["directUrl"] = ds.Url + jsonData.Set("directUrl", ds.Url) } datasources[ds.Name] = dsMap diff --git a/public/app/features/plugins/datasource_srv.ts b/public/app/features/plugins/datasource_srv.ts index d9c891ca8ba..6143a5e182b 100644 --- a/public/app/features/plugins/datasource_srv.ts +++ b/public/app/features/plugins/datasource_srv.ts @@ -65,8 +65,6 @@ export class DatasourceSrv { instanceSettings: dsConfig, }); - instance.id = dsConfig.id; - instance.name = name; instance.components = dsPlugin.components; instance.meta = dsConfig.meta; diff --git a/public/app/plugins/datasource/cloudwatch/datasource.ts b/public/app/plugins/datasource/cloudwatch/datasource.ts index 76ba2b8b9b6..bd2bd67248c 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.ts +++ b/public/app/plugins/datasource/cloudwatch/datasource.ts @@ -9,9 +9,8 @@ import { TemplateSrv } from 'app/features/templating/template_srv'; import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; // import * as moment from 'moment'; -export default class CloudWatchDatasource implements DataSourceApi { +export default class CloudWatchDatasource extends DataSourceApi { type: any; - name: any; proxyUrl: any; defaultRegion: any; standardStatistics: any; @@ -24,8 +23,8 @@ export default class CloudWatchDatasource implements DataSourceApi { - id: number; - name: string; +export default class Datasource extends DataSourceApi { azureMonitorDatasource: AzureMonitorDatasource; appInsightsDatasource: AppInsightsDatasource; azureLogAnalyticsDatasource: AzureLogAnalyticsDatasource; @@ -21,8 +19,7 @@ export default class Datasource implements DataSourceApi { +export class InputDatasource extends DataSourceApi { data: SeriesData[]; - // Filled in by grafana plugin system - name?: string; - - // Filled in by grafana plugin system - id?: number; - constructor(instanceSettings: DataSourceInstanceSettings) { - if (instanceSettings.jsonData) { - this.data = instanceSettings.jsonData.data; - } + super(instanceSettings); - if (!this.data) { - this.data = []; - } + this.data = instanceSettings.jsonData.data ? instanceSettings.jsonData.data : []; } getDescription(data: SeriesData[]): string { diff --git a/public/app/plugins/datasource/loki/datasource.test.ts b/public/app/plugins/datasource/loki/datasource.test.ts index a8c3ce27500..8b970f05f63 100644 --- a/public/app/plugins/datasource/loki/datasource.test.ts +++ b/public/app/plugins/datasource/loki/datasource.test.ts @@ -2,6 +2,8 @@ import LokiDatasource from './datasource'; import { LokiQuery } from './types'; import { getQueryOptions } from 'test/helpers/getQueryOptions'; import { SeriesData } from '@grafana/ui'; +import { BackendSrv } from 'app/core/services/backend_srv'; +import { TemplateSrv } from 'app/features/templating/template_srv'; describe('LokiDatasource', () => { const instanceSettings: any = { @@ -21,14 +23,15 @@ describe('LokiDatasource', () => { describe('when querying', () => { const backendSrvMock = { datasourceRequest: jest.fn() }; + const backendSrv = (backendSrvMock as unknown) as BackendSrv; - const templateSrvMock = { + const templateSrvMock = ({ getAdhocFilters: () => [], replace: a => a, - }; + } as unknown) as TemplateSrv; test('should use default max lines when no limit given', () => { - const ds = new LokiDatasource(instanceSettings, backendSrvMock, templateSrvMock); + const ds = new LokiDatasource(instanceSettings, backendSrv, templateSrvMock); backendSrvMock.datasourceRequest = jest.fn(() => Promise.resolve(testResp)); const options = getQueryOptions({ targets: [{ expr: 'foo', refId: 'B' }] }); @@ -41,7 +44,7 @@ describe('LokiDatasource', () => { test('should use custom max lines if limit is set', () => { const customData = { ...(instanceSettings.jsonData || {}), maxLines: 20 }; const customSettings = { ...instanceSettings, jsonData: customData }; - const ds = new LokiDatasource(customSettings, backendSrvMock, templateSrvMock); + const ds = new LokiDatasource(customSettings, backendSrv, templateSrvMock); backendSrvMock.datasourceRequest = jest.fn(() => Promise.resolve(testResp)); const options = getQueryOptions({ targets: [{ expr: 'foo', refId: 'B' }] }); @@ -54,7 +57,7 @@ describe('LokiDatasource', () => { test('should return series data', async done => { const customData = { ...(instanceSettings.jsonData || {}), maxLines: 20 }; const customSettings = { ...instanceSettings, jsonData: customData }; - const ds = new LokiDatasource(customSettings, backendSrvMock, templateSrvMock); + const ds = new LokiDatasource(customSettings, backendSrv, templateSrvMock); backendSrvMock.datasourceRequest = jest.fn(() => Promise.resolve(testResp)); const options = getQueryOptions({ @@ -77,7 +80,7 @@ describe('LokiDatasource', () => { describe('and call succeeds', () => { beforeEach(async () => { - const backendSrv = { + const backendSrv = ({ async datasourceRequest() { return Promise.resolve({ status: 200, @@ -86,8 +89,8 @@ describe('LokiDatasource', () => { }, }); }, - }; - ds = new LokiDatasource(instanceSettings, backendSrv, {}); + } as unknown) as BackendSrv; + ds = new LokiDatasource(instanceSettings, backendSrv, {} as TemplateSrv); result = await ds.testDatasource(); }); @@ -98,7 +101,7 @@ describe('LokiDatasource', () => { describe('and call fails with 401 error', () => { beforeEach(async () => { - const backendSrv = { + const backendSrv = ({ async datasourceRequest() { return Promise.reject({ statusText: 'Unauthorized', @@ -108,8 +111,8 @@ describe('LokiDatasource', () => { }, }); }, - }; - ds = new LokiDatasource(instanceSettings, backendSrv, {}); + } as unknown) as BackendSrv; + ds = new LokiDatasource(instanceSettings, backendSrv, {} as TemplateSrv); result = await ds.testDatasource(); }); @@ -121,7 +124,7 @@ describe('LokiDatasource', () => { describe('and call fails with 404 error', () => { beforeEach(async () => { - const backendSrv = { + const backendSrv = ({ async datasourceRequest() { return Promise.reject({ statusText: 'Not found', @@ -129,8 +132,8 @@ describe('LokiDatasource', () => { data: '404 page not found', }); }, - }; - ds = new LokiDatasource(instanceSettings, backendSrv, {}); + } as unknown) as BackendSrv; + ds = new LokiDatasource(instanceSettings, backendSrv, {} as TemplateSrv); result = await ds.testDatasource(); }); @@ -142,7 +145,7 @@ describe('LokiDatasource', () => { describe('and call fails with 502 error', () => { beforeEach(async () => { - const backendSrv = { + const backendSrv = ({ async datasourceRequest() { return Promise.reject({ statusText: 'Bad Gateway', @@ -150,8 +153,8 @@ describe('LokiDatasource', () => { data: '', }); }, - }; - ds = new LokiDatasource(instanceSettings, backendSrv, {}); + } as unknown) as BackendSrv; + ds = new LokiDatasource(instanceSettings, backendSrv, {} as TemplateSrv); result = await ds.testDatasource(); }); diff --git a/public/app/plugins/datasource/loki/datasource.ts b/public/app/plugins/datasource/loki/datasource.ts index 48e085682e9..4f22e41dead 100644 --- a/public/app/plugins/datasource/loki/datasource.ts +++ b/public/app/plugins/datasource/loki/datasource.ts @@ -9,8 +9,16 @@ import { logStreamToSeriesData } from './result_transformer'; import { formatQuery, parseQuery } from './query_utils'; // Types -import { PluginMeta, DataQueryRequest, SeriesData } from '@grafana/ui/src/types'; -import { LokiQuery } from './types'; +import { + PluginMeta, + DataQueryRequest, + SeriesData, + DataSourceApi, + DataSourceInstanceSettings, +} from '@grafana/ui/src/types'; +import { LokiQuery, LokiOptions } from './types'; +import { BackendSrv } from 'app/core/services/backend_srv'; +import { TemplateSrv } from 'app/features/templating/template_srv'; export const DEFAULT_MAX_LINES = 1000; @@ -30,12 +38,17 @@ function serializeParams(data: any) { .join('&'); } -export class LokiDatasource { +export class LokiDatasource extends DataSourceApi { languageProvider: LanguageProvider; maxLines: number; /** @ngInject */ - constructor(private instanceSettings, private backendSrv, private templateSrv) { + constructor( + private instanceSettings: DataSourceInstanceSettings, + private backendSrv: BackendSrv, + private templateSrv: TemplateSrv + ) { + super(instanceSettings); this.languageProvider = new LanguageProvider(this); const settingsData = instanceSettings.jsonData || {}; this.maxLines = parseInt(settingsData.maxLines, 10) || DEFAULT_MAX_LINES; diff --git a/public/app/plugins/datasource/loki/types.ts b/public/app/plugins/datasource/loki/types.ts index 6376f67b694..c4cfa3fcdc4 100644 --- a/public/app/plugins/datasource/loki/types.ts +++ b/public/app/plugins/datasource/loki/types.ts @@ -1,9 +1,13 @@ -import { DataQuery, Labels } from '@grafana/ui/src/types'; +import { DataQuery, Labels, DataSourceJsonData } from '@grafana/ui/src/types'; export interface LokiQuery extends DataQuery { expr: string; } +export interface LokiOptions extends DataSourceJsonData { + maxLines?: string; +} + export interface LokiLogsStream { labels: string; entries: LokiLogsStreamEntry[]; diff --git a/public/app/plugins/datasource/mixed/datasource.ts b/public/app/plugins/datasource/mixed/datasource.ts index 4f4b10dbe5b..5798695e844 100644 --- a/public/app/plugins/datasource/mixed/datasource.ts +++ b/public/app/plugins/datasource/mixed/datasource.ts @@ -1,11 +1,13 @@ import _ from 'lodash'; -import { DataSourceApi, DataQuery, DataQueryRequest } from '@grafana/ui'; +import { DataSourceApi, DataQuery, DataQueryRequest, DataSourceInstanceSettings } from '@grafana/ui'; import DatasourceSrv from 'app/features/plugins/datasource_srv'; -class MixedDatasource implements DataSourceApi { +class MixedDatasource extends DataSourceApi { /** @ngInject */ - constructor(private datasourceSrv: DatasourceSrv) {} + constructor(instanceSettings: DataSourceInstanceSettings, private datasourceSrv: DatasourceSrv) { + super(instanceSettings); + } query(options: DataQueryRequest) { const sets = _.groupBy(options.targets, 'datasource'); diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index e445eec9668..5b2470fbd1f 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -14,14 +14,15 @@ import { getQueryHints } from './query_hints'; import { expandRecordingRules } from './language_utils'; // Types -import { PromQuery } from './types'; -import { DataQueryRequest, DataSourceApi, AnnotationEvent } from '@grafana/ui/src/types'; +import { PromQuery, PromOptions } from './types'; +import { DataQueryRequest, DataSourceApi, AnnotationEvent, DataSourceInstanceSettings } from '@grafana/ui/src/types'; import { ExploreUrlState } from 'app/types/explore'; +import { TemplateSrv } from 'app/features/templating/template_srv'; +import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; -export class PrometheusDatasource implements DataSourceApi { +export class PrometheusDatasource extends DataSourceApi { type: string; editorSrc: string; - name: string; ruleMappings: { [index: string]: string }; url: string; directUrl: string; @@ -35,25 +36,32 @@ export class PrometheusDatasource implements DataSourceApi { resultTransformer: ResultTransformer; /** @ngInject */ - constructor(instanceSettings, private $q, private backendSrv: BackendSrv, private templateSrv, private timeSrv) { + constructor( + instanceSettings: DataSourceInstanceSettings, + private $q, + private backendSrv: BackendSrv, + private templateSrv: TemplateSrv, + private timeSrv: TimeSrv + ) { + super(instanceSettings); + this.type = 'prometheus'; this.editorSrc = 'app/features/prometheus/partials/query.editor.html'; - this.name = instanceSettings.name; this.url = instanceSettings.url; - this.directUrl = instanceSettings.directUrl; this.basicAuth = instanceSettings.basicAuth; this.withCredentials = instanceSettings.withCredentials; this.interval = instanceSettings.jsonData.timeInterval || '15s'; this.queryTimeout = instanceSettings.jsonData.queryTimeout; this.httpMethod = instanceSettings.jsonData.httpMethod || 'GET'; + this.directUrl = instanceSettings.jsonData.directUrl; this.resultTransformer = new ResultTransformer(templateSrv); this.ruleMappings = {}; this.languageProvider = new PrometheusLanguageProvider(this); } - init() { + init = () => { this.loadRules(); - } + }; getQueryDisplayText(query: PromQuery) { return query.expr; diff --git a/public/app/plugins/datasource/prometheus/specs/completer.test.ts b/public/app/plugins/datasource/prometheus/specs/completer.test.ts index 7d2e7de8648..693c850dafe 100644 --- a/public/app/plugins/datasource/prometheus/specs/completer.test.ts +++ b/public/app/plugins/datasource/prometheus/specs/completer.test.ts @@ -1,6 +1,10 @@ import { PromCompleter } from '../completer'; import { PrometheusDatasource } from '../datasource'; import { BackendSrv } from 'app/core/services/backend_srv'; +import { DataSourceInstanceSettings } from '@grafana/ui'; +import { PromOptions } from '../types'; +import { TemplateSrv } from 'app/features/templating/template_srv'; +import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; jest.mock('../datasource'); jest.mock('app/core/services/backend_srv'); @@ -16,7 +20,13 @@ describe('Prometheus editor completer', () => { const editor = {}; const backendSrv = {} as BackendSrv; - const datasourceStub = new PrometheusDatasource({}, {}, backendSrv, {}, {}); + const datasourceStub = new PrometheusDatasource( + {} as DataSourceInstanceSettings, + {}, + backendSrv, + {} as TemplateSrv, + {} as TimeSrv + ); datasourceStub.metadataRequest = jest.fn(() => Promise.resolve({ data: { data: [{ metric: { job: 'node', instance: 'localhost:9100' } }] } }) diff --git a/public/app/plugins/datasource/prometheus/specs/datasource.test.ts b/public/app/plugins/datasource/prometheus/specs/datasource.test.ts index 1c106482945..a38eb46ac03 100644 --- a/public/app/plugins/datasource/prometheus/specs/datasource.test.ts +++ b/public/app/plugins/datasource/prometheus/specs/datasource.test.ts @@ -8,6 +8,10 @@ import { prometheusSpecialRegexEscape, } from '../datasource'; import { dateTime } from '@grafana/ui/src/utils/moment_wrapper'; +import { DataSourceInstanceSettings } from '@grafana/ui'; +import { PromOptions } from '../types'; +import { TemplateSrv } from 'app/features/templating/template_srv'; +import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; jest.mock('../metric_find_query'); @@ -18,13 +22,13 @@ const DEFAULT_TEMPLATE_SRV_MOCK = { describe('PrometheusDatasource', () => { const ctx: any = {}; - const instanceSettings = { + const instanceSettings = ({ url: 'proxied', directUrl: 'direct', user: 'test', password: 'mupp', jsonData: {} as any, - }; + } as unknown) as DataSourceInstanceSettings; ctx.backendSrvMock = {}; @@ -347,27 +351,27 @@ const HOUR = 60 * MINUTE; const time = ({ hours = 0, seconds = 0, minutes = 0 }) => dateTime(hours * HOUR + minutes * MINUTE + seconds * SECOND); const ctx = {} as any; -const instanceSettings = { +const instanceSettings = ({ url: 'proxied', directUrl: 'direct', user: 'test', password: 'mupp', jsonData: { httpMethod: 'GET' }, -}; +} as unknown) as DataSourceInstanceSettings; const backendSrv = { datasourceRequest: jest.fn(), } as any; -const templateSrv = { +const templateSrv = ({ getAdhocFilters: () => [], replace: jest.fn(str => str), -}; +} as unknown) as TemplateSrv; -const timeSrv = { +const timeSrv = ({ timeRange: () => { return { to: { diff: () => 2000 }, from: '' }; }, -}; +} as unknown) as TimeSrv; describe('PrometheusDatasource', () => { describe('When querying prometheus with one target using query editor target spec', () => { @@ -1177,13 +1181,13 @@ describe('PrometheusDatasource', () => { describe('PrometheusDatasource for POST', () => { // const ctx = new helpers.ServiceTestContext(); - const instanceSettings = { + const instanceSettings = ({ url: 'proxied', directUrl: 'direct', user: 'test', password: 'mupp', jsonData: { httpMethod: 'POST' }, - }; + } as unknown) as DataSourceInstanceSettings; describe('When querying prometheus with one target using query editor target spec', () => { let results; diff --git a/public/app/plugins/datasource/prometheus/specs/metric_find_query.test.ts b/public/app/plugins/datasource/prometheus/specs/metric_find_query.test.ts index 3098d44aba2..aacfc5dd904 100644 --- a/public/app/plugins/datasource/prometheus/specs/metric_find_query.test.ts +++ b/public/app/plugins/datasource/prometheus/specs/metric_find_query.test.ts @@ -2,15 +2,17 @@ import { PrometheusDatasource } from '../datasource'; import PrometheusMetricFindQuery from '../metric_find_query'; import q from 'q'; import { toUtc } from '@grafana/ui/src/utils/moment_wrapper'; +import { DataSourceInstanceSettings } from '@grafana/ui'; +import { PromOptions } from '../types'; describe('PrometheusMetricFindQuery', () => { - const instanceSettings = { + const instanceSettings = ({ url: 'proxied', directUrl: 'direct', user: 'test', password: 'mupp', jsonData: { httpMethod: 'GET' }, - }; + } as unknown) as DataSourceInstanceSettings; const raw = { from: toUtc('2018-04-25 10:00'), to: toUtc('2018-04-25 11:00'), diff --git a/public/app/plugins/datasource/prometheus/types.ts b/public/app/plugins/datasource/prometheus/types.ts index 81fc4d8108d..e83029df835 100644 --- a/public/app/plugins/datasource/prometheus/types.ts +++ b/public/app/plugins/datasource/prometheus/types.ts @@ -1,5 +1,12 @@ -import { DataQuery } from '@grafana/ui/src/types'; +import { DataQuery, DataSourceJsonData } from '@grafana/ui/src/types'; export interface PromQuery extends DataQuery { expr: string; } + +export interface PromOptions extends DataSourceJsonData { + timeInterval: string; + queryTimeout: string; + httpMethod: string; + directUrl: string; +} diff --git a/public/app/plugins/datasource/stackdriver/datasource.ts b/public/app/plugins/datasource/stackdriver/datasource.ts index 73b61912e96..b2ca64668cc 100644 --- a/public/app/plugins/datasource/stackdriver/datasource.ts +++ b/public/app/plugins/datasource/stackdriver/datasource.ts @@ -2,11 +2,13 @@ import { stackdriverUnitMappings } from './constants'; import appEvents from 'app/core/app_events'; import _ from 'lodash'; import StackdriverMetricFindQuery from './StackdriverMetricFindQuery'; -import { StackdriverQuery, MetricDescriptor } from './types'; -import { DataSourceApi, DataQueryRequest } from '@grafana/ui/src/types'; +import { StackdriverQuery, MetricDescriptor, StackdriverOptions } from './types'; +import { DataSourceApi, DataQueryRequest, DataSourceInstanceSettings, ScopedVars } from '@grafana/ui/src/types'; +import { BackendSrv } from 'app/core/services/backend_srv'; +import { TemplateSrv } from 'app/features/templating/template_srv'; +import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; -export default class StackdriverDatasource implements DataSourceApi { - id: number; +export default class StackdriverDatasource extends DataSourceApi { url: string; baseUrl: string; projectName: string; @@ -15,10 +17,15 @@ export default class StackdriverDatasource implements DataSourceApi, + private backendSrv: BackendSrv, + private templateSrv: TemplateSrv, + private timeSrv: TimeSrv + ) { + super(instanceSettings); this.baseUrl = `/stackdriver/`; this.url = instanceSettings.url; - this.id = instanceSettings.id; this.projectName = instanceSettings.jsonData.defaultProject || ''; this.authenticationType = instanceSettings.jsonData.authenticationType || 'jwt'; this.metricTypes = []; @@ -62,7 +69,7 @@ export default class StackdriverDatasource implements DataSourceApi { return this.templateSrv.replace(f, scopedVars || {}, 'regex'); }); diff --git a/public/app/plugins/datasource/stackdriver/specs/datasource.test.ts b/public/app/plugins/datasource/stackdriver/specs/datasource.test.ts index b9c8ac71064..09aac570029 100644 --- a/public/app/plugins/datasource/stackdriver/specs/datasource.test.ts +++ b/public/app/plugins/datasource/stackdriver/specs/datasource.test.ts @@ -3,26 +3,30 @@ import { metricDescriptors } from './testData'; import { TemplateSrv } from 'app/features/templating/template_srv'; import { CustomVariable } from 'app/features/templating/all'; import { toUtc } from '@grafana/ui/src/utils/moment_wrapper'; +import { DataSourceInstanceSettings } from '@grafana/ui'; +import { StackdriverOptions } from '../types'; +import { BackendSrv } from 'app/core/services/backend_srv'; +import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; describe('StackdriverDataSource', () => { - const instanceSettings = { + const instanceSettings = ({ jsonData: { defaultProject: 'testproject', }, - }; + } as unknown) as DataSourceInstanceSettings; const templateSrv = new TemplateSrv(); - const timeSrv = {}; + const timeSrv = {} as TimeSrv; describe('when performing testDataSource', () => { describe('and call to stackdriver api succeeds', () => { let ds; let result; beforeEach(async () => { - const backendSrv = { + const backendSrv = ({ async datasourceRequest() { return Promise.resolve({ status: 200 }); }, - }; + } as unknown) as BackendSrv; ds = new StackdriverDataSource(instanceSettings, backendSrv, templateSrv, timeSrv); result = await ds.testDatasource(); }); @@ -35,9 +39,9 @@ describe('StackdriverDataSource', () => { let ds; let result; beforeEach(async () => { - const backendSrv = { + const backendSrv = ({ datasourceRequest: async () => Promise.resolve({ status: 200, data: metricDescriptors }), - }; + } as unknown) as BackendSrv; ds = new StackdriverDataSource(instanceSettings, backendSrv, templateSrv, timeSrv); result = await ds.testDatasource(); }); @@ -50,7 +54,7 @@ describe('StackdriverDataSource', () => { let ds; let result; beforeEach(async () => { - const backendSrv = { + const backendSrv = ({ datasourceRequest: async () => Promise.reject({ statusText: 'Bad Request', @@ -58,7 +62,7 @@ describe('StackdriverDataSource', () => { error: { code: 400, message: 'Field interval.endTime had an invalid value' }, }, }), - }; + } as unknown) as BackendSrv; ds = new StackdriverDataSource(instanceSettings, backendSrv, templateSrv, timeSrv); result = await ds.testDatasource(); }); @@ -103,9 +107,9 @@ describe('StackdriverDataSource', () => { }; beforeEach(() => { - const backendSrv = { + const backendSrv = ({ datasourceRequest: async () => Promise.resolve({ status: 200, data: response }), - }; + } as unknown) as BackendSrv; ds = new StackdriverDataSource(instanceSettings, backendSrv, templateSrv, timeSrv); }); @@ -122,7 +126,7 @@ describe('StackdriverDataSource', () => { let ds; let result; beforeEach(async () => { - const backendSrv = { + const backendSrv = ({ async datasourceRequest() { return Promise.resolve({ data: { @@ -139,7 +143,7 @@ describe('StackdriverDataSource', () => { }, }); }, - }; + } as unknown) as BackendSrv; ds = new StackdriverDataSource(instanceSettings, backendSrv, templateSrv, timeSrv); result = await ds.getMetricTypes(); }); @@ -155,12 +159,14 @@ describe('StackdriverDataSource', () => { }); }); + const noopBackendSrv = ({} as unknown) as BackendSrv; + describe('when interpolating a template variable for the filter', () => { let interpolated; describe('and is single value variable', () => { beforeEach(() => { const filterTemplateSrv = initTemplateSrv('filtervalue1'); - const ds = new StackdriverDataSource(instanceSettings, {}, filterTemplateSrv, timeSrv); + const ds = new StackdriverDataSource(instanceSettings, noopBackendSrv, filterTemplateSrv, timeSrv); interpolated = ds.interpolateFilters(['resource.label.zone', '=~', '${test}'], {}); }); @@ -173,7 +179,7 @@ describe('StackdriverDataSource', () => { describe('and is multi value variable', () => { beforeEach(() => { const filterTemplateSrv = initTemplateSrv(['filtervalue1', 'filtervalue2'], true); - const ds = new StackdriverDataSource(instanceSettings, {}, filterTemplateSrv, timeSrv); + const ds = new StackdriverDataSource(instanceSettings, noopBackendSrv, filterTemplateSrv, timeSrv); interpolated = ds.interpolateFilters(['resource.label.zone', '=~', '[[test]]'], {}); }); @@ -189,7 +195,7 @@ describe('StackdriverDataSource', () => { describe('and is single value variable', () => { beforeEach(() => { const groupByTemplateSrv = initTemplateSrv('groupby1'); - const ds = new StackdriverDataSource(instanceSettings, {}, groupByTemplateSrv, timeSrv); + const ds = new StackdriverDataSource(instanceSettings, noopBackendSrv, groupByTemplateSrv, timeSrv); interpolated = ds.interpolateGroupBys(['[[test]]'], {}); }); @@ -202,7 +208,7 @@ describe('StackdriverDataSource', () => { describe('and is multi value variable', () => { beforeEach(() => { const groupByTemplateSrv = initTemplateSrv(['groupby1', 'groupby2'], true); - const ds = new StackdriverDataSource(instanceSettings, {}, groupByTemplateSrv, timeSrv); + const ds = new StackdriverDataSource(instanceSettings, noopBackendSrv, groupByTemplateSrv, timeSrv); interpolated = ds.interpolateGroupBys(['[[test]]'], {}); }); @@ -217,7 +223,7 @@ describe('StackdriverDataSource', () => { describe('unit parsing', () => { let ds, res; beforeEach(() => { - ds = new StackdriverDataSource(instanceSettings, {}, templateSrv, timeSrv); + ds = new StackdriverDataSource(instanceSettings, noopBackendSrv, templateSrv, timeSrv); }); describe('when theres only one target', () => { describe('and the stackdriver unit doesnt have a corresponding grafana unit', () => { diff --git a/public/app/plugins/datasource/stackdriver/types.ts b/public/app/plugins/datasource/stackdriver/types.ts index b9a6893d4bd..f9a829279c3 100644 --- a/public/app/plugins/datasource/stackdriver/types.ts +++ b/public/app/plugins/datasource/stackdriver/types.ts @@ -1,4 +1,4 @@ -import { DataQuery } from '@grafana/ui/src/types'; +import { DataQuery, DataSourceJsonData } from '@grafana/ui/src/types'; export enum MetricFindQueryTypes { Services = 'services', @@ -40,6 +40,11 @@ export interface StackdriverQuery extends DataQuery { view?: string; } +export interface StackdriverOptions extends DataSourceJsonData { + defaultProject?: string; + authenticationType?: string; +} + export interface AnnotationTarget { defaultProject: string; metricType: string; diff --git a/public/app/plugins/datasource/testdata/datasource.ts b/public/app/plugins/datasource/testdata/datasource.ts index 1d8ff8cda26..0f69028f784 100644 --- a/public/app/plugins/datasource/testdata/datasource.ts +++ b/public/app/plugins/datasource/testdata/datasource.ts @@ -17,13 +17,12 @@ export interface TestDataRegistry { [key: string]: TestData[]; } -export class TestDataDatasource implements DataSourceApi { - id: number; +export class TestDataDatasource extends DataSourceApi { streams = new StreamHandler(); /** @ngInject */ constructor(instanceSettings: DataSourceInstanceSettings) { - this.id = instanceSettings.id; + super(instanceSettings); } query(options: DataQueryRequest, observer: DataStreamObserver) { diff --git a/public/test/mocks/datasource_srv.ts b/public/test/mocks/datasource_srv.ts index 4a0e9809aad..5c4d8944cb6 100644 --- a/public/test/mocks/datasource_srv.ts +++ b/public/test/mocks/datasource_srv.ts @@ -1,4 +1,4 @@ -import { DataSourceApi, DataQueryRequest, DataQueryResponse } from '@grafana/ui'; +import { DataSourceApi, DataQueryRequest, DataQueryResponse, DataSourceInstanceSettings } from '@grafana/ui'; export class DatasourceSrvMock { constructor(private defaultDS: DataSourceApi, private datasources: { [name: string]: DataSourceApi }) { @@ -17,14 +17,15 @@ export class DatasourceSrvMock { } } -export class MockDataSourceApi implements DataSourceApi { - name: string; - +export class MockDataSourceApi extends DataSourceApi { result: DataQueryResponse = { data: [] }; queryResolver: Promise; - constructor(DataQueryResponse, name?: string) { - this.name = name ? name : 'MockDataSourceApi'; + constructor(name?: string, result?: DataQueryResponse) { + super({ name: name ? name : 'MockDataSourceApi' } as DataSourceInstanceSettings); + if (result) { + this.result = result; + } } query(request: DataQueryRequest): Promise {