diff --git a/public/app/core/services/segment_srv.ts b/public/app/core/services/segment_srv.ts index d093a5570d6..fc37aab6356 100644 --- a/public/app/core/services/segment_srv.ts +++ b/public/app/core/services/segment_srv.ts @@ -95,7 +95,7 @@ export function uiSegmentSrv(this: any, $sce: any, templateSrv: any) { }); if (addTemplateVars) { - _.each(templateSrv.variables, variable => { + _.each(templateSrv.getVariables(), variable => { if (variableTypeFilter === void 0 || variableTypeFilter === variable.type) { segments.unshift(self.newSegment({ type: 'value', value: '$' + variable.name, expandable: true })); } diff --git a/public/app/features/panel/panellinks/link_srv.ts b/public/app/features/panel/panellinks/link_srv.ts index 4998cbdb8dc..3f24007f6cb 100644 --- a/public/app/features/panel/panellinks/link_srv.ts +++ b/public/app/features/panel/panellinks/link_srv.ts @@ -8,16 +8,16 @@ import { getConfig } from 'app/core/config'; import locationUtil from 'app/core/utils/location_util'; import { DataLinkBuiltInVars } from '@grafana/ui'; import { - DataLink, - KeyValue, - deprecationWarning, - LinkModel, DataFrame, - ScopedVars, - FieldType, + DataLink, + deprecationWarning, Field, - VariableSuggestion, + FieldType, + KeyValue, + LinkModel, + ScopedVars, VariableOrigin, + VariableSuggestion, VariableSuggestionsScope, } from '@grafana/data'; @@ -77,7 +77,7 @@ const buildLabelPath = (label: string) => { }; export const getPanelLinksVariableSuggestions = (): VariableSuggestion[] => [ - ...templateSrv.variables.map(variable => ({ + ...templateSrv.getVariables().map(variable => ({ value: variable.name as string, label: variable.name, origin: VariableOrigin.Template, diff --git a/public/app/features/plugins/datasource_srv.ts b/public/app/features/plugins/datasource_srv.ts index d6cd3ee0c75..d6bde0d58db 100644 --- a/public/app/features/plugins/datasource_srv.ts +++ b/public/app/features/plugins/datasource_srv.ts @@ -1,20 +1,18 @@ // Libraries import sortBy from 'lodash/sortBy'; import coreModule from 'app/core/core_module'; - // Services & Utils import config from 'app/core/config'; import { importDataSourcePlugin } from './plugin_loader'; import { DataSourceSrv as DataSourceService, getDataSourceSrv as getDataSourceService } from '@grafana/runtime'; - // Types -import { DataSourceApi, DataSourceSelectItem, ScopedVars, AppEvents } from '@grafana/data'; +import { AppEvents, DataSourceApi, DataSourceSelectItem, ScopedVars } from '@grafana/data'; import { auto } from 'angular'; import { TemplateSrv } from '../templating/template_srv'; import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; - // Pretend Datasource import { expressionDatasource } from 'app/features/expressions/ExpressionDatasource'; +import { DataSourceVariableModel } from '../templating/types'; export class DatasourceSrv implements DataSourceService { datasources: Record = {}; @@ -163,11 +161,13 @@ export class DatasourceSrv implements DataSourceService { addDataSourceVariables(list: any[]) { // look for data source variables - this.templateSrv.variables + this.templateSrv + .getVariables() .filter(variable => variable.type === 'datasource') - .forEach(variable => { + .forEach((variable: DataSourceVariableModel) => { const first = variable.current.value === 'default' ? config.defaultDatasource : variable.current.value; - const ds = config.datasources[first]; + const index = (first as unknown) as string; + const ds = config.datasources[index]; if (ds) { const key = `$${variable.name}`; diff --git a/public/app/features/plugins/specs/datasource_srv.test.ts b/public/app/features/plugins/specs/datasource_srv.test.ts index d9c645342cb..041c9692dd8 100644 --- a/public/app/features/plugins/specs/datasource_srv.test.ts +++ b/public/app/features/plugins/specs/datasource_srv.test.ts @@ -1,11 +1,11 @@ import config from 'app/core/config'; import 'app/features/plugins/datasource_srv'; import { DatasourceSrv } from 'app/features/plugins/datasource_srv'; -import { PluginMeta, DataSourcePluginMeta } from '@grafana/data'; +import { DataSourcePluginMeta, PluginMeta } from '@grafana/data'; // Datasource variable $datasource with current value 'BBB' const templateSrv: any = { - variables: [ + getVariables: () => [ { type: 'datasource', name: 'datasource', diff --git a/public/app/features/templating/template_srv.ts b/public/app/features/templating/template_srv.ts index c91b2f8fe6d..42581774786 100644 --- a/public/app/features/templating/template_srv.ts +++ b/public/app/features/templating/template_srv.ts @@ -1,11 +1,12 @@ import kbn from 'app/core/utils/kbn'; import _ from 'lodash'; import { escapeHtml } from 'app/core/utils/text'; -import { ScopedVars, TimeRange } from '@grafana/data'; -import { getVariableWithName, getFilteredVariables } from '../variables/state/selectors'; +import { deprecationWarning, ScopedVars, TimeRange } from '@grafana/data'; +import { getFilteredVariables, getVariableClones, getVariableWithName } from '../variables/state/selectors'; import { getConfig } from 'app/core/config'; import { variableRegex } from './utils'; import { isAdHoc } from '../variables/guard'; +import { VariableModel } from './types'; function luceneEscape(value: string) { return value.replace(/([\!\*\+\-\=<>\s\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g, '\\$1'); @@ -16,8 +17,7 @@ interface FieldAccessorCache { } export class TemplateSrv { - variables: any[]; - + private _variables: any[]; private regex = variableRegex; private index: any = {}; private grafanaVariables: any = {}; @@ -28,11 +28,11 @@ export class TemplateSrv { constructor() { this.builtIns['__interval'] = { text: '1s', value: '1s' }; this.builtIns['__interval_ms'] = { text: '100', value: '100' }; - this.variables = []; + this._variables = []; } init(variables: any, timeRange?: TimeRange) { - this.variables = variables; + this._variables = variables; this.timeRange = timeRange; this.updateIndex(); } @@ -41,10 +41,28 @@ export class TemplateSrv { return this.builtIns.__interval.value; } + /** + * @deprecated: this instance variable should not be used and will be removed in future releases + * + * Use getVariables function instead + */ + get variables(): any[] { + deprecationWarning('template_srv.ts', 'variables', 'getVariables'); + return this.getVariables(); + } + + getVariables(): VariableModel[] { + if (getConfig().featureToggles.newVariables) { + return getVariableClones(); + } + + return this._variables; + } + updateIndex() { const existsOrEmpty = (value: any) => value || value === ''; - this.index = this.variables.reduce((acc, currentValue) => { + this.index = this._variables.reduce((acc, currentValue) => { if (currentValue.current && (currentValue.current.isNone || existsOrEmpty(currentValue.current.value))) { acc[currentValue.name] = currentValue; } @@ -345,7 +363,7 @@ export class TemplateSrv { } fillVariableValuesForUrl(params: any, scopedVars?: ScopedVars) { - _.each(this.variables, variable => { + _.each(this._variables, variable => { if (scopedVars && scopedVars[variable.name] !== void 0) { if (scopedVars[variable.name].skipUrlSync) { return; @@ -387,8 +405,8 @@ export class TemplateSrv { if (getConfig().featureToggles.newVariables) { return getFilteredVariables(isAdHoc); } - if (Array.isArray(this.variables)) { - return this.variables.filter(isAdHoc); + if (Array.isArray(this._variables)) { + return this._variables.filter(isAdHoc); } return []; }; diff --git a/public/app/plugins/datasource/cloudwatch/datasource.ts b/public/app/plugins/datasource/cloudwatch/datasource.ts index 16ab6851059..df6ae702838 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.ts +++ b/public/app/plugins/datasource/cloudwatch/datasource.ts @@ -7,20 +7,21 @@ import { AppNotificationTimeout } from 'app/types'; import { store } from 'app/store/store'; import kbn from 'app/core/utils/kbn'; import { + DataQueryRequest, + DataSourceApi, + DataSourceInstanceSettings, dateMath, ScopedVars, - toDataFrame, TimeRange, - DataSourceApi, - DataQueryRequest, - DataSourceInstanceSettings, + toDataFrame, } from '@grafana/data'; import { getBackendSrv } from '@grafana/runtime'; import { TemplateSrv } from 'app/features/templating/template_srv'; import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { ThrottlingErrorMessage } from './components/ThrottlingErrorMessage'; import memoizedDebounce from './memoizedDebounce'; -import { CloudWatchQuery, CloudWatchJsonData } from './types'; +import { CloudWatchJsonData, CloudWatchQuery } from './types'; +import { VariableWithMultiSupport } from 'app/features/templating/types'; const displayAlert = (datasourceName: string, region: string) => store.dispatch( @@ -121,7 +122,7 @@ export default class CloudWatchDatasource extends DataSourceApi `$${v.name}`); + return this.templateSrv.getVariables().map(v => `$${v.name}`); } getPeriod(target: any, options: any) { @@ -562,9 +563,11 @@ export default class CloudWatchDatasource extends DataSourceApi name === this.templateSrv.getVariableName(value)); + const valueVar = this.templateSrv + .getVariables() + .find(({ name }) => name === this.templateSrv.getVariableName(value)); if (valueVar) { - if (valueVar.multi) { + if (((valueVar as unknown) as VariableWithMultiSupport).multi) { const values = this.templateSrv.replace(value, scopedVars, 'pipe').split('|'); return { ...result, [key]: values }; } @@ -577,8 +580,10 @@ export default class CloudWatchDatasource extends DataSourceApi name === this.templateSrv.getVariableName(target)); - if (variable && variable.multi) { + const variable = this.templateSrv + .getVariables() + .find(({ name }) => name === this.templateSrv.getVariableName(target)); + if (variable && ((variable as unknown) as VariableWithMultiSupport).multi) { this.debouncedCustomAlert( 'CloudWatch templating error', `Multi template variables are not supported for ${fieldName || target}` diff --git a/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts b/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts index 5000b4d2549..cea7657a58b 100644 --- a/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts +++ b/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.ts @@ -207,7 +207,7 @@ export class CloudWatchQueryParameterCtrl { }); if (addTemplateVars) { - _.each(templateSrv.variables, variable => { + _.each(templateSrv.getVariables(), variable => { segments.unshift( uiSegmentSrv.newSegment({ type: 'template', diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/annotations_query_ctrl.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/annotations_query_ctrl.ts index 6827edc0c82..7edcac84529 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/annotations_query_ctrl.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/annotations_query_ctrl.ts @@ -74,6 +74,6 @@ export class AzureMonitorAnnotationsQueryCtrl { }; get templateVariables() { - return this.templateSrv.variables.map((t: any) => '$' + t.name); + return this.templateSrv.getVariables().map((t: any) => '$' + t.name); } } diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts index 167286d02ac..f2e1f4d6525 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts @@ -7,8 +7,7 @@ import kbn from 'app/core/utils/kbn'; import { TemplateSrv } from 'app/features/templating/template_srv'; import { auto, IPromise } from 'angular'; -import { DataFrame } from '@grafana/data'; -import { PanelEvents } from '@grafana/data'; +import { DataFrame, PanelEvents } from '@grafana/data'; export interface ResultFormat { text: string; @@ -570,7 +569,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl { }; get templateVariables() { - return this.templateSrv.variables.map(t => '$' + t.name); + return this.templateSrv.getVariables().map(t => '$' + t.name); } /* Application Insights Section */ diff --git a/public/app/plugins/datasource/graphite/query_ctrl.ts b/public/app/plugins/datasource/graphite/query_ctrl.ts index 4737ec95103..2d6aec80db3 100644 --- a/public/app/plugins/datasource/graphite/query_ctrl.ts +++ b/public/app/plugins/datasource/graphite/query_ctrl.ts @@ -158,7 +158,7 @@ export class GraphiteQueryCtrl extends QueryCtrl { } // add template variables - _.eachRight(this.templateSrv.variables, variable => { + _.eachRight(this.templateSrv.getVariables(), variable => { altSegments.unshift( this.uiSegmentSrv.newSegment({ type: 'template', @@ -369,7 +369,7 @@ export class GraphiteQueryCtrl extends QueryCtrl { return this.datasource.getTagValuesAutoComplete(tagExpressions, tagKey, valuePrefix).then((values: any[]) => { const altValues = _.map(values, 'text'); // Add template variables as additional values - _.eachRight(this.templateSrv.variables, variable => { + _.eachRight(this.templateSrv.getVariables(), variable => { altValues.push('${' + variable.name + ':regex}'); }); return mapToDropdownOptions(altValues); diff --git a/public/app/plugins/datasource/influxdb/query_ctrl.ts b/public/app/plugins/datasource/influxdb/query_ctrl.ts index 36528616642..19c9aec7792 100644 --- a/public/app/plugins/datasource/influxdb/query_ctrl.ts +++ b/public/app/plugins/datasource/influxdb/query_ctrl.ts @@ -267,7 +267,7 @@ export class InfluxQueryCtrl extends QueryCtrl { }); if (addTemplateVars) { - for (const variable of this.templateSrv.variables) { + for (const variable of this.templateSrv.getVariables()) { segments.unshift( this.uiSegmentSrv.newSegment({ type: 'value', diff --git a/public/app/plugins/datasource/mysql/query_ctrl.ts b/public/app/plugins/datasource/mysql/query_ctrl.ts index 19d21852e90..b3f089ca452 100644 --- a/public/app/plugins/datasource/mysql/query_ctrl.ts +++ b/public/app/plugins/datasource/mysql/query_ctrl.ts @@ -9,6 +9,7 @@ import { auto } from 'angular'; import { TemplateSrv } from 'app/features/templating/template_srv'; import { CoreEvents } from 'app/types'; import { PanelEvents } from '@grafana/data'; +import { VariableWithMultiSupport } from 'app/features/templating/types'; export interface QueryMeta { sql: string; @@ -301,10 +302,10 @@ export class MysqlQueryCtrl extends QueryCtrl { }); if (config.addTemplateVars) { - for (const variable of this.templateSrv.variables) { + for (const variable of this.templateSrv.getVariables()) { let value; value = '$' + variable.name; - if (config.templateQuoter && variable.multi === false) { + if (config.templateQuoter && ((variable as unknown) as VariableWithMultiSupport).multi === false) { value = config.templateQuoter(value); } diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts index 0a99fd570f1..8696ee9c6a2 100644 --- a/public/app/plugins/datasource/postgres/query_ctrl.ts +++ b/public/app/plugins/datasource/postgres/query_ctrl.ts @@ -9,6 +9,7 @@ import { auto } from 'angular'; import { TemplateSrv } from 'app/features/templating/template_srv'; import { CoreEvents } from 'app/types'; import { PanelEvents } from '@grafana/data'; +import { VariableWithMultiSupport } from 'app/features/templating/types'; export interface QueryMeta { sql: string; @@ -333,10 +334,10 @@ export class PostgresQueryCtrl extends QueryCtrl { }); if (config.addTemplateVars) { - for (const variable of this.templateSrv.variables) { + for (const variable of this.templateSrv.getVariables()) { let value; value = '$' + variable.name; - if (config.templateQuoter && variable.multi === false) { + if (config.templateQuoter && ((variable as unknown) as VariableWithMultiSupport).multi === false) { value = config.templateQuoter(value); } diff --git a/public/app/plugins/datasource/stackdriver/components/VariableQueryEditor.test.tsx b/public/app/plugins/datasource/stackdriver/components/VariableQueryEditor.test.tsx index da2f4a2d8e7..a5751380e16 100644 --- a/public/app/plugins/datasource/stackdriver/components/VariableQueryEditor.test.tsx +++ b/public/app/plugins/datasource/stackdriver/components/VariableQueryEditor.test.tsx @@ -4,6 +4,7 @@ import renderer from 'react-test-renderer'; import { StackdriverVariableQueryEditor } from './VariableQueryEditor'; import { VariableQueryProps } from 'app/types/plugins'; import { MetricFindQueryTypes } from '../types'; +import { VariableModel } from 'app/features/templating/types'; jest.mock('../functions', () => ({ getMetricTypes: (): any => ({ metricTypes: [], selectedMetricType: '' }), @@ -18,7 +19,7 @@ const props: VariableQueryProps = { getProjects: async (): Promise => [], getMetricTypes: async (p: any): Promise => [], }, - templateSrv: { replace: (s: string) => s, variables: [] }, + templateSrv: { replace: (s: string) => s, getVariables: () => ([] as unknown) as VariableModel[] }, }; describe('VariableQueryEditor', () => { diff --git a/public/app/plugins/datasource/stackdriver/components/VariableQueryEditor.tsx b/public/app/plugins/datasource/stackdriver/components/VariableQueryEditor.tsx index c05bfb5c617..8a914716b16 100644 --- a/public/app/plugins/datasource/stackdriver/components/VariableQueryEditor.tsx +++ b/public/app/plugins/datasource/stackdriver/components/VariableQueryEditor.tsx @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react'; import { VariableQueryProps } from 'app/types/plugins'; import { SimpleSelect } from './'; -import { getMetricTypes, getLabelKeys, extractServicesFromMetricDescriptors } from '../functions'; +import { extractServicesFromMetricDescriptors, getLabelKeys, getMetricTypes } from '../functions'; import { MetricFindQueryTypes, VariableQueryData } from '../types'; export class StackdriverVariableQueryEditor extends PureComponent { @@ -143,7 +143,7 @@ export class StackdriverVariableQueryEditor extends PureComponent ({ + const templateVariables = this.props.templateSrv.getVariables().map((v: any) => ({ name: `$${v.name}`, value: `$${v.name}`, })); diff --git a/public/app/plugins/datasource/stackdriver/datasource.ts b/public/app/plugins/datasource/stackdriver/datasource.ts index e8ccd26ff40..aa546a2be38 100644 --- a/public/app/plugins/datasource/stackdriver/datasource.ts +++ b/public/app/plugins/datasource/stackdriver/datasource.ts @@ -2,13 +2,13 @@ import { stackdriverUnitMappings } from './constants'; import appEvents from 'app/core/app_events'; import _ from 'lodash'; import StackdriverMetricFindQuery from './StackdriverMetricFindQuery'; -import { StackdriverQuery, MetricDescriptor, StackdriverOptions, Filter, VariableQueryData } from './types'; +import { Filter, MetricDescriptor, StackdriverOptions, StackdriverQuery, VariableQueryData } from './types'; import { - DataSourceApi, DataQueryRequest, + DataQueryResponse, + DataSourceApi, DataSourceInstanceSettings, ScopedVars, - DataQueryResponse, } from '@grafana/data'; import { getBackendSrv } from '@grafana/runtime'; import { TemplateSrv } from 'app/features/templating/template_srv'; @@ -38,7 +38,7 @@ export default class StackdriverDatasource extends DataSourceApi `$${v.name}`); + return this.templateSrv.getVariables().map(v => `$${v.name}`); } async getTimeSeries(options: DataQueryRequest) {