diff --git a/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx b/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx
index 316f78d45ca..01cd8c02fbd 100644
--- a/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx
+++ b/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx
@@ -4,7 +4,7 @@ import Cascader from 'rc-cascader';
import PluginPrism from 'slate-prism';
import Prism from 'prismjs';
-import { TypeaheadOutput } from 'app/types/explore';
+import { TypeaheadOutput, HistoryItem } from 'app/types/explore';
// dom also includes Element polyfills
import { getNextCharacter, getPreviousCousin } from 'app/features/explore/utils/dom';
@@ -13,6 +13,7 @@ import RunnerPlugin from 'app/features/explore/slate-plugins/runner';
import QueryField, { TypeaheadInput, QueryFieldState } from 'app/features/explore/QueryField';
import { PromQuery } from '../types';
import { CancelablePromise, makePromiseCancelable } from 'app/core/utils/CancelablePromise';
+import { ExploreDataSourceApi, ExploreQueryFieldProps } from '@grafana/ui';
const HISTOGRAM_GROUP = '__histograms__';
const METRIC_MARK = 'metric';
@@ -86,15 +87,8 @@ interface CascaderOption {
disabled?: boolean;
}
-interface PromQueryFieldProps {
- datasource: any;
- error?: string | JSX.Element;
- initialQuery: PromQuery;
- hint?: any;
- history?: any[];
- onClickHintFix?: (action: any) => void;
- onPressEnter?: () => void;
- onQueryChange?: (value: PromQuery, override?: boolean) => void;
+interface PromQueryFieldProps extends ExploreQueryFieldProps
{
+ history: HistoryItem[];
}
interface PromQueryFieldState {
@@ -116,7 +110,7 @@ class PromQueryField extends React.PureComponent node.type === 'code_block',
getSyntax: node => 'promql',
@@ -174,20 +168,21 @@ class PromQueryField extends React.PureComponent {
// Send text change to parent
- const { initialQuery, onQueryChange } = this.props;
+ const { query, onQueryChange, onExecuteQuery } = this.props;
if (onQueryChange) {
- const query: PromQuery = {
- ...initialQuery,
- expr: value,
- };
- onQueryChange(query, override);
+ const nextQuery: PromQuery = { ...query, expr: value };
+ onQueryChange(nextQuery);
+
+ if (override && onExecuteQuery) {
+ onExecuteQuery();
+ }
}
};
onClickHintFix = () => {
- const { hint, onClickHintFix } = this.props;
- if (onClickHintFix && hint && hint.fix) {
- onClickHintFix(hint.fix.action);
+ const { hint, onExecuteHint } = this.props;
+ if (onExecuteHint && hint && hint.fix) {
+ onExecuteHint(hint.fix.action);
}
};
@@ -242,7 +237,7 @@ class PromQueryField extends React.PureComponent void;
-}
-
-export default class PromStart extends PureComponent {
+export default class PromStart extends PureComponent {
render() {
return (
diff --git a/public/app/plugins/datasource/stackdriver/components/QueryEditor.tsx b/public/app/plugins/datasource/stackdriver/components/QueryEditor.tsx
index 94521041416..c3bd9212b21 100644
--- a/public/app/plugins/datasource/stackdriver/components/QueryEditor.tsx
+++ b/public/app/plugins/datasource/stackdriver/components/QueryEditor.tsx
@@ -10,21 +10,21 @@ import { Alignments } from './Alignments';
import { AlignmentPeriods } from './AlignmentPeriods';
import { AliasBy } from './AliasBy';
import { Help } from './Help';
-import { Target, MetricDescriptor } from '../types';
+import { StackdriverQuery, MetricDescriptor } from '../types';
import { getAlignmentPickerData } from '../functions';
import StackdriverDatasource from '../datasource';
import { SelectOptionItem } from '@grafana/ui';
export interface Props {
- onQueryChange: (target: Target) => void;
+ onQueryChange: (target: StackdriverQuery) => void;
onExecuteQuery: () => void;
- target: Target;
+ target: StackdriverQuery;
events: any;
datasource: StackdriverDatasource;
templateSrv: TemplateSrv;
}
-interface State extends Target {
+interface State extends StackdriverQuery {
alignOptions: SelectOptionItem[];
lastQuery: string;
lastQueryError: string;
diff --git a/public/app/plugins/datasource/stackdriver/datasource.ts b/public/app/plugins/datasource/stackdriver/datasource.ts
index 025955105a7..4c1f07e0a06 100644
--- a/public/app/plugins/datasource/stackdriver/datasource.ts
+++ b/public/app/plugins/datasource/stackdriver/datasource.ts
@@ -2,9 +2,10 @@ import { stackdriverUnitMappings } from './constants';
import appEvents from 'app/core/app_events';
import _ from 'lodash';
import StackdriverMetricFindQuery from './StackdriverMetricFindQuery';
-import { MetricDescriptor } from './types';
+import { StackdriverQuery, MetricDescriptor } from './types';
+import { DataSourceApi, DataQueryOptions } from '@grafana/ui/src/types';
-export default class StackdriverDatasource {
+export default class StackdriverDatasource implements DataSourceApi
{
id: number;
url: string;
baseUrl: string;
@@ -39,9 +40,7 @@ export default class StackdriverDatasource {
alignmentPeriod: this.templateSrv.replace(t.alignmentPeriod, options.scopedVars || {}),
groupBys: this.interpolateGroupBys(t.groupBys, options.scopedVars),
view: t.view || 'FULL',
- filters: (t.filters || []).map(f => {
- return this.templateSrv.replace(f, options.scopedVars || {});
- }),
+ filters: this.interpolateFilters(t.filters, options.scopedVars),
aliasBy: this.templateSrv.replace(t.aliasBy, options.scopedVars || {}),
type: 'timeSeriesQuery',
};
@@ -63,7 +62,13 @@ export default class StackdriverDatasource {
}
}
- async getLabels(metricType, refId) {
+ interpolateFilters(filters: string[], scopedVars: object) {
+ return (filters || []).map(f => {
+ return this.templateSrv.replace(f, scopedVars || {}, 'regex');
+ });
+ }
+
+ async getLabels(metricType: string, refId: string) {
const response = await this.getTimeSeries({
targets: [
{
@@ -103,7 +108,7 @@ export default class StackdriverDatasource {
return unit;
}
- async query(options) {
+ async query(options: DataQueryOptions) {
const result = [];
const data = await this.getTimeSeries(options);
if (data.results) {
diff --git a/public/app/plugins/datasource/stackdriver/img/stackdriver_logo.png b/public/app/plugins/datasource/stackdriver/img/stackdriver_logo.png
deleted file mode 100644
index cd52e773deb..00000000000
Binary files a/public/app/plugins/datasource/stackdriver/img/stackdriver_logo.png and /dev/null differ
diff --git a/public/app/plugins/datasource/stackdriver/img/stackdriver_logo.svg b/public/app/plugins/datasource/stackdriver/img/stackdriver_logo.svg
new file mode 100644
index 00000000000..93878f20a06
--- /dev/null
+++ b/public/app/plugins/datasource/stackdriver/img/stackdriver_logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/app/plugins/datasource/stackdriver/plugin.json b/public/app/plugins/datasource/stackdriver/plugin.json
index e68100c0e59..1ee3d57e9b1 100644
--- a/public/app/plugins/datasource/stackdriver/plugin.json
+++ b/public/app/plugins/datasource/stackdriver/plugin.json
@@ -14,8 +14,8 @@
"description": "Google Stackdriver Datasource for Grafana",
"version": "1.0.0",
"logos": {
- "small": "img/stackdriver_logo.png",
- "large": "img/stackdriver_logo.png"
+ "small": "img/stackdriver_logo.svg",
+ "large": "img/stackdriver_logo.svg"
},
"author": {
"name": "Grafana Project",
diff --git a/public/app/plugins/datasource/stackdriver/query_ctrl.ts b/public/app/plugins/datasource/stackdriver/query_ctrl.ts
index c6a8a4d9782..3a2d0bb970a 100644
--- a/public/app/plugins/datasource/stackdriver/query_ctrl.ts
+++ b/public/app/plugins/datasource/stackdriver/query_ctrl.ts
@@ -1,7 +1,7 @@
import _ from 'lodash';
import { QueryCtrl } from 'app/plugins/sdk';
-import { Target } from './types';
+import { StackdriverQuery } from './types';
import { TemplateSrv } from 'app/features/templating/template_srv';
export class StackdriverQueryCtrl extends QueryCtrl {
@@ -16,7 +16,7 @@ export class StackdriverQueryCtrl extends QueryCtrl {
this.onExecuteQuery = this.onExecuteQuery.bind(this);
}
- onQueryChange(target: Target) {
+ onQueryChange(target: StackdriverQuery) {
Object.assign(this.target, target);
}
diff --git a/public/app/plugins/datasource/stackdriver/specs/datasource.test.ts b/public/app/plugins/datasource/stackdriver/specs/datasource.test.ts
index 46cdd77b7a9..032f10d8ca5 100644
--- a/public/app/plugins/datasource/stackdriver/specs/datasource.test.ts
+++ b/public/app/plugins/datasource/stackdriver/specs/datasource.test.ts
@@ -1,7 +1,8 @@
import StackdriverDataSource from '../datasource';
import { metricDescriptors } from './testData';
import moment from 'moment';
-import { TemplateSrvStub } from 'test/specs/helpers';
+import { TemplateSrv } from 'app/features/templating/template_srv';
+import { CustomVariable } from 'app/features/templating/all';
describe('StackdriverDataSource', () => {
const instanceSettings = {
@@ -9,7 +10,7 @@ describe('StackdriverDataSource', () => {
defaultProject: 'testproject',
},
};
- const templateSrv = new TemplateSrvStub();
+ const templateSrv = new TemplateSrv();
const timeSrv = {};
describe('when performing testDataSource', () => {
@@ -154,15 +155,41 @@ describe('StackdriverDataSource', () => {
});
});
+ 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);
+ interpolated = ds.interpolateFilters(['resource.label.zone', '=~', '${test}'], {});
+ });
+
+ it('should replace the variable with the value', () => {
+ expect(interpolated.length).toBe(3);
+ expect(interpolated[2]).toBe('filtervalue1');
+ });
+ });
+
+ describe('and is multi value variable', () => {
+ beforeEach(() => {
+ const filterTemplateSrv = initTemplateSrv(['filtervalue1', 'filtervalue2'], true);
+ const ds = new StackdriverDataSource(instanceSettings, {}, filterTemplateSrv, timeSrv);
+ interpolated = ds.interpolateFilters(['resource.label.zone', '=~', '[[test]]'], {});
+ });
+
+ it('should replace the variable with a regex expression', () => {
+ expect(interpolated[2]).toBe('(filtervalue1|filtervalue2)');
+ });
+ });
+ });
+
describe('when interpolating a template variable for group bys', () => {
let interpolated;
describe('and is single value variable', () => {
beforeEach(() => {
- templateSrv.data = {
- test: 'groupby1',
- };
- const ds = new StackdriverDataSource(instanceSettings, {}, templateSrv, timeSrv);
+ const groupByTemplateSrv = initTemplateSrv('groupby1');
+ const ds = new StackdriverDataSource(instanceSettings, {}, groupByTemplateSrv, timeSrv);
interpolated = ds.interpolateGroupBys(['[[test]]'], {});
});
@@ -174,10 +201,8 @@ describe('StackdriverDataSource', () => {
describe('and is multi value variable', () => {
beforeEach(() => {
- templateSrv.data = {
- test: 'groupby1,groupby2',
- };
- const ds = new StackdriverDataSource(instanceSettings, {}, templateSrv, timeSrv);
+ const groupByTemplateSrv = initTemplateSrv(['groupby1', 'groupby2'], true);
+ const ds = new StackdriverDataSource(instanceSettings, {}, groupByTemplateSrv, timeSrv);
interpolated = ds.interpolateGroupBys(['[[test]]'], {});
});
@@ -241,3 +266,19 @@ describe('StackdriverDataSource', () => {
});
});
});
+function initTemplateSrv(values: any, multi = false) {
+ const templateSrv = new TemplateSrv();
+ templateSrv.init([
+ new CustomVariable(
+ {
+ name: 'test',
+ current: {
+ value: values,
+ },
+ multi: multi,
+ },
+ {}
+ ),
+ ]);
+ return templateSrv;
+}
diff --git a/public/app/plugins/datasource/stackdriver/types.ts b/public/app/plugins/datasource/stackdriver/types.ts
index 29b12b4289d..b9a6893d4bd 100644
--- a/public/app/plugins/datasource/stackdriver/types.ts
+++ b/public/app/plugins/datasource/stackdriver/types.ts
@@ -1,3 +1,5 @@
+import { DataQuery } from '@grafana/ui/src/types';
+
export enum MetricFindQueryTypes {
Services = 'services',
MetricTypes = 'metricTypes',
@@ -20,20 +22,22 @@ export interface VariableQueryData {
services: Array<{ value: string; name: string }>;
}
-export interface Target {
- defaultProject: string;
- unit: string;
+export interface StackdriverQuery extends DataQuery {
+ defaultProject?: string;
+ unit?: string;
metricType: string;
- service: string;
+ service?: string;
refId: string;
crossSeriesReducer: string;
- alignmentPeriod: string;
+ alignmentPeriod?: string;
perSeriesAligner: string;
- groupBys: string[];
- filters: string[];
- aliasBy: string;
+ groupBys?: string[];
+ filters?: string[];
+ aliasBy?: string;
metricKind: string;
valueType: string;
+ datasourceId?: number;
+ view?: string;
}
export interface AnnotationTarget {
diff --git a/public/app/routes/GrafanaCtrl.ts b/public/app/routes/GrafanaCtrl.ts
index 9157c189ab2..c2d5dd4d69d 100644
--- a/public/app/routes/GrafanaCtrl.ts
+++ b/public/app/routes/GrafanaCtrl.ts
@@ -274,6 +274,28 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
if (popover.length > 0 && target.parents('.graph-legend').length === 0) {
popover.hide();
}
+
+ // hide time picker
+ const timePickerDropDownIsOpen = elem.find('.gf-timepicker-dropdown').length > 0;
+ if (timePickerDropDownIsOpen) {
+ const targetIsInTimePickerDropDown = target.parents('.gf-timepicker-dropdown').length > 0;
+ const targetIsInTimePickerNav = target.parents('.gf-timepicker-nav').length > 0;
+ const targetIsDatePickerRowBtn = target.parents('td[id^="datepicker-"]').length > 0;
+ const targetIsDatePickerHeaderBtn = target.parents('button[id^="datepicker-"]').length > 0;
+
+ if (
+ targetIsInTimePickerNav ||
+ targetIsInTimePickerDropDown ||
+ targetIsDatePickerRowBtn ||
+ targetIsDatePickerHeaderBtn
+ ) {
+ return;
+ }
+
+ scope.$apply(() => {
+ scope.appEvent('closeTimepicker');
+ });
+ }
});
},
};
diff --git a/public/app/store/configureStore.ts b/public/app/store/configureStore.ts
index dc9a478adf3..570a387cd74 100644
--- a/public/app/store/configureStore.ts
+++ b/public/app/store/configureStore.ts
@@ -1,6 +1,6 @@
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
import thunk from 'redux-thunk';
-// import { createLogger } from 'redux-logger';
+import { createLogger } from 'redux-logger';
import sharedReducers from 'app/core/reducers';
import alertingReducers from 'app/features/alerting/state/reducers';
import teamsReducers from 'app/features/teams/state/reducers';
@@ -39,7 +39,7 @@ export function configureStore() {
if (process.env.NODE_ENV !== 'production') {
// DEV builds we had the logger middleware
- setStore(createStore(rootReducer, {}, composeEnhancers(applyMiddleware(thunk))));
+ setStore(createStore(rootReducer, {}, composeEnhancers(applyMiddleware(thunk, createLogger()))));
} else {
setStore(createStore(rootReducer, {}, composeEnhancers(applyMiddleware(thunk))));
}
diff --git a/public/app/types/explore.ts b/public/app/types/explore.ts
index 3abbc652c0d..9c8d977c3ad 100644
--- a/public/app/types/explore.ts
+++ b/public/app/types/explore.ts
@@ -1,5 +1,14 @@
+import { ComponentClass } from 'react';
import { Value } from 'slate';
-import { RawTimeRange, TimeRange, DataQuery, DataSourceSelectItem, DataSourceApi, QueryHint } from '@grafana/ui';
+import {
+ RawTimeRange,
+ TimeRange,
+ DataQuery,
+ DataSourceSelectItem,
+ DataSourceApi,
+ QueryHint,
+ ExploreStartPageProps,
+} from '@grafana/ui';
import { Emitter } from 'app/core/core';
import { LogsModel } from 'app/core/logs_model';
@@ -102,7 +111,7 @@ export interface ExploreItemState {
/**
* React component to be shown when no queries have been run yet, e.g., for a query language cheat sheet.
*/
- StartPage?: any;
+ StartPage?: ComponentClass;
/**
* Width used for calculating the graph interval (can't have more datapoints than pixels)
*/
@@ -144,10 +153,10 @@ export interface ExploreItemState {
*/
history: HistoryItem[];
/**
- * Initial queries for this Explore, e.g., set via URL. Each query will be
- * converted to a query row. Query edits should be tracked in `modifiedQueries` though.
+ * Queries for this Explore, e.g., set via URL. Each query will be
+ * converted to a query row.
*/
- initialQueries: DataQuery[];
+ queries: DataQuery[];
/**
* True if this Explore area has been initialized.
* Used to distinguish URL state injection versus split view state injection.
@@ -162,12 +171,6 @@ export interface ExploreItemState {
* Log query result to be displayed in the logs result viewer.
*/
logsResult?: LogsModel;
- /**
- * Copy of `initialQueries` that tracks user edits.
- * Don't connect this property to a react component as it is updated on every query change.
- * Used when running queries. Needs to be reset to `initialQueries` when those are reset as well.
- */
- modifiedQueries: DataQuery[];
/**
* Query intervals for graph queries to determine how many datapoints to return.
* Needs to be updated when `datasourceInstance` or `containerWidth` is changed.
@@ -229,6 +232,11 @@ export interface ExploreItemState {
* Table model that combines all query table results into a single table.
*/
tableResult?: TableModel;
+
+ /**
+ * React keys for rendering of QueryRows
+ */
+ queryKeys: string[];
}
export interface ExploreUIState {
diff --git a/public/img/icons_dark_theme/icon_advanced.svg b/public/img/icons_dark_theme/icon_advanced.svg
index 5fd18a86dd5..dea3ddff685 100644
--- a/public/img/icons_dark_theme/icon_advanced.svg
+++ b/public/img/icons_dark_theme/icon_advanced.svg
@@ -4,7 +4,7 @@
diff --git a/public/img/icons_dark_theme/icon_advanced_active.svg b/public/img/icons_dark_theme/icon_advanced_active.svg
index 80672a2595b..1227ddc868c 100644
--- a/public/img/icons_dark_theme/icon_advanced_active.svg
+++ b/public/img/icons_dark_theme/icon_advanced_active.svg
@@ -5,7 +5,7 @@
width="121px" height="100px" viewBox="0 0 121 100" style="enable-background:new 0 0 121 100;" xml:space="preserve">
diff --git a/public/img/icons_dark_theme/icon_alerting_active.svg b/public/img/icons_dark_theme/icon_alerting_active.svg
index 5c70638f64c..4966761ba28 100644
--- a/public/img/icons_dark_theme/icon_alerting_active.svg
+++ b/public/img/icons_dark_theme/icon_alerting_active.svg
@@ -5,7 +5,7 @@
width="121px" height="100px" viewBox="0 0 121 100" style="enable-background:new 0 0 121 100;" xml:space="preserve">
diff --git a/public/img/icons_dark_theme/icon_query.svg b/public/img/icons_dark_theme/icon_query.svg
index fab09779936..7b9dce299b5 100644
--- a/public/img/icons_dark_theme/icon_query.svg
+++ b/public/img/icons_dark_theme/icon_query.svg
@@ -4,7 +4,7 @@
diff --git a/public/img/icons_dark_theme/icon_query_active.svg b/public/img/icons_dark_theme/icon_query_active.svg
index ed8141b51bf..4831d9c52c8 100644
--- a/public/img/icons_dark_theme/icon_query_active.svg
+++ b/public/img/icons_dark_theme/icon_query_active.svg
@@ -5,7 +5,7 @@
width="121px" height="100px" viewBox="0 0 121 100" style="enable-background:new 0 0 121 100;" xml:space="preserve">
diff --git a/public/img/icons_dark_theme/icon_visualize_active.svg b/public/img/icons_dark_theme/icon_visualize_active.svg
index 43207ebaae4..31b5938652e 100644
--- a/public/img/icons_dark_theme/icon_visualize_active.svg
+++ b/public/img/icons_dark_theme/icon_visualize_active.svg
@@ -5,7 +5,7 @@
width="121px" height="100px" viewBox="0 0 121 100" style="enable-background:new 0 0 121 100;" xml:space="preserve">
diff --git a/public/sass/base/_icons.scss b/public/sass/base/_icons.scss
index a60259ac0f2..a2649b31fcd 100644
--- a/public/sass/base/_icons.scss
+++ b/public/sass/base/_icons.scss
@@ -212,7 +212,7 @@
padding-right: 5px;
}
-.panel-editor-tabs {
+.panel-editor-tabs, .add-panel-widget__icon {
.gicon-advanced-active {
background-image: url('../img/icons_#{$theme-name}_theme/icon_advanced_active.svg');
}