From 2046309de51fb85376ce50c9dc9c9da4f0cbfbee Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Wed, 19 Dec 2018 10:42:57 +0100 Subject: [PATCH 01/68] rewrite angular view --- .../features/alerting/partials/alert_tab.html | 336 ++++++++---------- 1 file changed, 155 insertions(+), 181 deletions(-) diff --git a/public/app/features/alerting/partials/alert_tab.html b/public/app/features/alerting/partials/alert_tab.html index 0e4e48a89a9..5fcc7ae5e44 100644 --- a/public/app/features/alerting/partials/alert_tab.html +++ b/public/app/features/alerting/partials/alert_tab.html @@ -1,191 +1,165 @@ -
-
- +
+
+ {{ctrl.error}} +
+
+
+
+
+
+ Name + +
+
+
+ Evaluate every + +
+
+ + + + If an alert rule has a configured For and the query violates the configured + threshold it + will first go from OK to Pending. + Going from OK to Pending Grafana will not send any notifications. Once the alert + rule + has + been firing for more than For duration, it will change to Alerting and send alert + notifications. + +
+
+
+
+
+
+
+
Conditions
+
+
+
+ + WHEN +
+
+ + + OF +
+
+ + +
+
+ + + + +
+
+ +
+
+
+ +
+
+
+ If no data or all values are null + SET STATE TO +
+ +
+
-
-
-
- {{ctrl.error}} -
+
+ If execution error or timeout + SET STATE TO +
+ +
+
-
-
Alert Config
-
- Name - -
-
-
- Evaluate every - -
-
- - - - If an alert rule has a configured For and the query violates the configured threshold it will first go from OK to Pending. - Going from OK to Pending Grafana will not send any notifications. Once the alert rule has been firing for more than For duration, it will change to Alerting and send alert notifications. - -
-
-
+
+ +
+
-
-
Conditions
-
-
- - WHEN -
-
- - - OF -
-
- - -
-
- - - - -
-
- -
-
+
+ Evaluating rule +
-
- -
-
- -
-
- If no data or all values are null - SET STATE TO -
- -
-
- -
- If execution error or timeout - SET STATE TO -
- -
-
- -
- -
-
- -
- Evaluating rule -
- -
- -
-
- -
-
Notifications
-
-
- Send to - +
+ +
+
+
+
+
Notifications
+
+
+
+ Send to +  {{nc.name}}  - + - -
-
-
- Message - -
-
- -
- -
- State history (last 50 state changes) -
- -
-
- No state changes recorded -
- -
    -
  1. -
    - -
    -
    -
    -
    - {{al.stateModel.text}} -
    -
    - {{al.info}} -
    -
    - {{al.time}} -
    -
  2. -
-
-
-
+ +
+
+
+ Message + +
+
+
-
-
Panel has no alert rule defined
- -
-
+
+
Panel has no alert rule defined
+ +
From f3ba3b4df0e4a71e32b0e70024a11ba9204665c5 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Wed, 19 Dec 2018 13:09:53 +0100 Subject: [PATCH 02/68] render editor toolbar buttons --- .../features/dashboard/dashgrid/AlertTab.tsx | 22 ++++++++++++++++++- .../dashboard/dashgrid/PanelEditor.tsx | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/AlertTab.tsx b/public/app/features/dashboard/dashgrid/AlertTab.tsx index 7df7864c758..f9f565222d8 100644 --- a/public/app/features/dashboard/dashgrid/AlertTab.tsx +++ b/public/app/features/dashboard/dashgrid/AlertTab.tsx @@ -3,9 +3,11 @@ import React, { PureComponent } from 'react'; import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader'; import { EditorTabBody } from './EditorTabBody'; import 'app/features/alerting/AlertTabCtrl'; +import { PanelModel } from '../panel_model'; interface Props { angularPanel?: AngularComponent; + panel: PanelModel; } export class AlertTab extends PureComponent { @@ -63,8 +65,26 @@ export class AlertTab extends PureComponent { } render() { + const { alert } = this.props.panel; + + const stateHistory = { + title: 'State history', + render: () => { + return
State history
; + }, + }; + + const deleteAlert = { + title: 'Delete button', + render: () => { + return
Hello
; + }, + }; + + const toolbarItems = alert ? [deleteAlert, stateHistory] : []; + return ( - +
(this.element = element)} /> ); diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index a746d6c4b91..b3d94f43487 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -54,7 +54,7 @@ export class PanelEditor extends PureComponent { case 'queries': return ; case 'alert': - return ; + return ; case 'visualization': return ( Date: Fri, 21 Dec 2018 11:24:31 +0100 Subject: [PATCH 03/68] adding extra props From 6aacd0734b6c4ec99913c0772bd9329dee9a12da Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Fri, 21 Dec 2018 11:57:21 +0100 Subject: [PATCH 04/68] typings and renamings --- .../features/dashboard/dashgrid/AlertTab.tsx | 8 +++--- .../dashboard/dashgrid/EditorTabBody.tsx | 26 +++++++++++++------ .../dashboard/dashgrid/QueriesTab.tsx | 19 +++++++------- .../dashboard/dashgrid/VisualizationTab.tsx | 10 +++---- 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/AlertTab.tsx b/public/app/features/dashboard/dashgrid/AlertTab.tsx index f9f565222d8..ee7d320a85b 100644 --- a/public/app/features/dashboard/dashgrid/AlertTab.tsx +++ b/public/app/features/dashboard/dashgrid/AlertTab.tsx @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react'; -import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader'; -import { EditorTabBody } from './EditorTabBody'; +import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader'; +import { EditorTabBody, EditorToolbarView, ToolbarButtonType } from './EditorTabBody'; import 'app/features/alerting/AlertTabCtrl'; import { PanelModel } from '../panel_model'; @@ -67,11 +67,12 @@ export class AlertTab extends PureComponent { render() { const { alert } = this.props.panel; - const stateHistory = { + const stateHistory: EditorToolbarView = { title: 'State history', render: () => { return
State history
; }, + buttonType: ToolbarButtonType.View, }; const deleteAlert = { @@ -79,6 +80,7 @@ export class AlertTab extends PureComponent { render: () => { return
Hello
; }, + buttonType: ToolbarButtonType.Action, }; const toolbarItems = alert ? [deleteAlert, stateHistory] : []; diff --git a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx b/public/app/features/dashboard/dashgrid/EditorTabBody.tsx index 7606d327405..2cd247ed704 100644 --- a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx +++ b/public/app/features/dashboard/dashgrid/EditorTabBody.tsx @@ -10,21 +10,28 @@ interface Props { children: JSX.Element; heading: string; renderToolbar?: () => JSX.Element; - toolbarItems?: EditorToolBarView[]; + toolbarItems?: EditorToolbarView[]; } -export interface EditorToolBarView { +export enum ToolbarButtonType { + Action = 'action', + View = 'view', +} + +export interface EditorToolbarView { title?: string; heading?: string; imgSrc?: string; icon?: string; disabled?: boolean; onClick?: () => void; - render: (closeFunction?: any) => JSX.Element | JSX.Element[]; + render?: (closeFunction?: any) => JSX.Element | JSX.Element[]; + action?: () => void; + buttonType: ToolbarButtonType; } interface State { - openView?: EditorToolBarView; + openView?: EditorToolbarView; isOpen: boolean; fadeIn: boolean; } @@ -48,7 +55,7 @@ export class EditorTabBody extends PureComponent { this.setState({ fadeIn: true }); } - onToggleToolBarView = (item: EditorToolBarView) => { + onToggleToolBarView = (item: EditorToolbarView) => { this.setState({ openView: item, isOpen: !this.state.isOpen, @@ -74,12 +81,15 @@ export class EditorTabBody extends PureComponent { return state; } - renderButton(view: EditorToolBarView) { + renderButton(view: EditorToolbarView) { const onClick = () => { if (view.onClick) { view.onClick(); } - this.onToggleToolBarView(view); + + if (view.buttonType !== ToolbarButtonType.Action) { + this.onToggleToolBarView(view); + } }; return ( @@ -91,7 +101,7 @@ export class EditorTabBody extends PureComponent { ); } - renderOpenView(view: EditorToolBarView) { + renderOpenView(view: EditorToolbarView) { return ( {view.render()} diff --git a/public/app/features/dashboard/dashgrid/QueriesTab.tsx b/public/app/features/dashboard/dashgrid/QueriesTab.tsx index 9ad0bb3cadd..98287e4888e 100644 --- a/public/app/features/dashboard/dashgrid/QueriesTab.tsx +++ b/public/app/features/dashboard/dashgrid/QueriesTab.tsx @@ -1,26 +1,23 @@ // Libraries -import React, { SFC, PureComponent } from 'react'; +import React, { PureComponent, SFC } from 'react'; import _ from 'lodash'; - // Components import './../../panel/metrics_tab'; -import { EditorTabBody } from './EditorTabBody'; +import { EditorTabBody, EditorToolbarView, ToolbarButtonType } from './EditorTabBody'; import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker'; import { QueryInspector } from './QueryInspector'; import { QueryOptions } from './QueryOptions'; import { AngularQueryComponentScope } from 'app/features/panel/metrics_tab'; import { PanelOptionSection } from './PanelOptionSection'; - // Services import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; -import { getBackendSrv, BackendSrv } from 'app/core/services/backend_srv'; -import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader'; +import { BackendSrv, getBackendSrv } from 'app/core/services/backend_srv'; +import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader'; import config from 'app/core/config'; - // Types import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; -import { DataSourceSelectItem, DataQuery } from 'app/types'; +import { DataQuery, DataSourceSelectItem } from 'app/types'; import { PluginHelp } from 'app/core/components/PluginHelp/PluginHelp'; interface Props { @@ -204,15 +201,17 @@ export class QueriesTab extends PureComponent { const { panel } = this.props; const { currentDS, isAddingMixed } = this.state; - const queryInspector = { + const queryInspector: EditorToolbarView = { title: 'Query Inspector', render: this.renderQueryInspector, + buttonType: ToolbarButtonType.View, }; - const dsHelp = { + const dsHelp: EditorToolbarView = { heading: 'Help', icon: 'fa fa-question', render: this.renderHelp, + buttonType: ToolbarButtonType.View, }; return ( diff --git a/public/app/features/dashboard/dashgrid/VisualizationTab.tsx b/public/app/features/dashboard/dashgrid/VisualizationTab.tsx index 42d9bf6a6eb..8a57dd17190 100644 --- a/public/app/features/dashboard/dashgrid/VisualizationTab.tsx +++ b/public/app/features/dashboard/dashgrid/VisualizationTab.tsx @@ -1,16 +1,13 @@ // Libraries import React, { PureComponent } from 'react'; - // Utils & Services -import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader'; - +import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader'; // Components -import { EditorTabBody } from './EditorTabBody'; +import { EditorTabBody, EditorToolbarView, ToolbarButtonType } from './EditorTabBody'; import { VizTypePicker } from './VizTypePicker'; import { PluginHelp } from 'app/core/components/PluginHelp/PluginHelp'; import { FadeIn } from 'app/core/components/Animations/FadeIn'; import { PanelOptionSection } from './PanelOptionSection'; - // Types import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; @@ -206,10 +203,11 @@ export class VisualizationTab extends PureComponent { const { plugin } = this.props; const { isVizPickerOpen, searchQuery } = this.state; - const pluginHelp = { + const pluginHelp: EditorToolbarView = { heading: 'Help', icon: 'fa fa-question', render: this.renderHelp, + buttonType: ToolbarButtonType.View, }; return ( From 4002f80ab880e34b60a855b03820f4b21d1f9814 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Fri, 21 Dec 2018 14:56:49 +0100 Subject: [PATCH 05/68] delete works --- .../features/alerting/partials/alert_tab.html | 4 +- .../features/dashboard/dashgrid/AlertTab.tsx | 44 ++++++++++++++----- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/public/app/features/alerting/partials/alert_tab.html b/public/app/features/alerting/partials/alert_tab.html index 5fcc7ae5e44..cbddb93207a 100644 --- a/public/app/features/alerting/partials/alert_tab.html +++ b/public/app/features/alerting/partials/alert_tab.html @@ -1,4 +1,4 @@ -
+
{{ctrl.error}}
@@ -154,7 +154,7 @@
-
+
Panel has no alert rule defined
- -
-
-
Panel has no alert rule defined
- -
-
diff --git a/public/app/features/dashboard/dashgrid/AlertTab.tsx b/public/app/features/dashboard/dashgrid/AlertTab.tsx index 2a715536ca8..5ccfa791328 100644 --- a/public/app/features/dashboard/dashgrid/AlertTab.tsx +++ b/public/app/features/dashboard/dashgrid/AlertTab.tsx @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react'; - import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader'; import { EditorTabBody, EditorToolbarView, ToolbarButtonType } from './EditorTabBody'; +import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import appEvents from 'app/core/app_events'; import { PanelModel } from '../panel_model'; import 'app/features/alerting/AlertTabCtrl'; @@ -33,7 +33,7 @@ export class AlertTab extends PureComponent { } shouldLoadAlertTab() { - return this.props.angularPanel && this.element; + return this.props.angularPanel && this.element && !this.component; } componentWillUnmount() { @@ -93,6 +93,7 @@ export class AlertTab extends PureComponent { panel.thresholds = []; this.panelCtrl.alertState = null; this.panelCtrl.render(); + this.forceUpdate(); }, }); }, @@ -100,15 +101,31 @@ export class AlertTab extends PureComponent { }; }; + onAddAlert = () => { + this.panelCtrl._enableAlert(); + this.component.digest(); + this.forceUpdate(); + }; + render() { const { alert } = this.props.panel; const toolbarItems = alert ? [this.stateHistory(), this.deleteAlert()] : []; + const model = { + title: 'Panel has no alert rule defined', + icon: 'icon-gf icon-gf-alert', + onClick: this.onAddAlert, + buttonTitle: 'Create Alert', + }; + //TODO move add button react from angular and add condition to render angular view return ( -
(this.element = element)} /> + <> +
(this.element = element)} /> + {!alert && } + ); } From 757d6f322e211da19af70b9f7455e3dbeb667243 Mon Sep 17 00:00:00 2001 From: David Kaltschmidt Date: Wed, 2 Jan 2019 11:32:12 +0000 Subject: [PATCH 10/68] s/initialDatasourceId/initialDatasource/ --- public/app/core/utils/explore.test.ts | 8 ++++---- public/app/core/utils/explore.ts | 2 +- public/app/features/explore/Explore.tsx | 14 +++++++------- public/app/types/explore.ts | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/public/app/core/utils/explore.test.ts b/public/app/core/utils/explore.test.ts index fcd92681004..a3b08516d16 100644 --- a/public/app/core/utils/explore.test.ts +++ b/public/app/core/utils/explore.test.ts @@ -68,7 +68,7 @@ describe('state functions', () => { it('returns url parameter value for a state object', () => { const state = { ...DEFAULT_EXPLORE_STATE, - initialDatasourceId: 'foo', + initialDatasource: 'foo', range: { from: 'now-5h', to: 'now', @@ -93,7 +93,7 @@ describe('state functions', () => { it('returns url parameter value for a state object', () => { const state = { ...DEFAULT_EXPLORE_STATE, - initialDatasourceId: 'foo', + initialDatasource: 'foo', range: { from: 'now-5h', to: 'now', @@ -119,7 +119,7 @@ describe('state functions', () => { it('can parse the serialized state into the original state', () => { const state = { ...DEFAULT_EXPLORE_STATE, - initialDatasourceId: 'foo', + initialDatasource: 'foo', range: { from: 'now - 5h', to: 'now', @@ -143,7 +143,7 @@ describe('state functions', () => { const resultState = { ...rest, datasource: DEFAULT_EXPLORE_STATE.datasource, - initialDatasourceId: datasource, + initialDatasource: datasource, initialQueries: queries, }; diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index 1927c616d50..bea166075dc 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -105,7 +105,7 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState { export function serializeStateToUrlParam(state: ExploreState, compact?: boolean): string { const urlState: ExploreUrlState = { - datasource: state.initialDatasourceId, + datasource: state.initialDatasource, queries: state.initialQueries.map(clearQueryKeys), range: state.range, }; diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx index 11e9becb9bb..a905034f302 100644 --- a/public/app/features/explore/Explore.tsx +++ b/public/app/features/explore/Explore.tsx @@ -95,7 +95,7 @@ export class Explore extends React.PureComponent { /** * Set via URL or local storage */ - initialDatasourceId: string; + initialDatasource: string; /** * Current query expressions of the rows including their modifications, used for running queries. * Not kept in component state to prevent edit-render roundtrips. @@ -121,7 +121,7 @@ export class Explore extends React.PureComponent { initialQueries = splitState.initialQueries; } else { const { datasource, queries, range } = props.urlState as ExploreUrlState; - const initialDatasourceId = datasource || store.get(LAST_USED_DATASOURCE_KEY); + const initialDatasource = datasource || store.get(LAST_USED_DATASOURCE_KEY); initialQueries = ensureQueries(queries); const initialRange = { from: parseTime(range.from), to: parseTime(range.to) } || { ...DEFAULT_RANGE }; // Millies step for helper bar charts @@ -134,7 +134,7 @@ export class Explore extends React.PureComponent { exploreDatasources: [], graphInterval: initialGraphInterval, graphResult: [], - initialDatasourceId, + initialDatasource, initialQueries, history: [], logsResult: null, @@ -158,7 +158,7 @@ export class Explore extends React.PureComponent { async componentDidMount() { const { datasourceSrv } = this.props; - const { initialDatasourceId } = this.state; + const { initialDatasource } = this.state; if (!datasourceSrv) { throw new Error('No datasource service passed as props.'); } @@ -174,8 +174,8 @@ export class Explore extends React.PureComponent { this.setState({ datasourceLoading: true, exploreDatasources }); // Priority for datasource preselection: URL, localstorage, default datasource let datasource; - if (initialDatasourceId) { - datasource = await datasourceSrv.get(initialDatasourceId); + if (initialDatasource) { + datasource = await datasourceSrv.get(initialDatasource); } else { datasource = await datasourceSrv.get(); } @@ -260,7 +260,7 @@ export class Explore extends React.PureComponent { supportsLogs, supportsTable, datasourceLoading: false, - initialDatasourceId: datasource.name, + initialDatasource: datasource.name, initialQueries: nextQueries, logsHighlighterExpressions: undefined, showingStartPage: Boolean(StartPage), diff --git a/public/app/types/explore.ts b/public/app/types/explore.ts index 7ea0462900d..c2c59d35f5b 100644 --- a/public/app/types/explore.ts +++ b/public/app/types/explore.ts @@ -159,7 +159,7 @@ export interface ExploreState { graphInterval: number; // in ms graphResult?: any[]; history: HistoryItem[]; - initialDatasourceId?: string; + initialDatasource?: string; initialQueries: DataQuery[]; logsHighlighterExpressions?: string[]; logsResult?: LogsModel; From e7d9bbf78138e91bbd940771f242e6659dca5285 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Wed, 2 Jan 2019 15:22:22 +0100 Subject: [PATCH 11/68] state history tab --- .../features/dashboard/dashgrid/AlertTab.tsx | 6 +- .../dashboard/dashgrid/PanelEditor.tsx | 2 +- .../dashboard/dashgrid/StateHistory.tsx | 85 +++++++++++++++++++ 3 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 public/app/features/dashboard/dashgrid/StateHistory.tsx diff --git a/public/app/features/dashboard/dashgrid/AlertTab.tsx b/public/app/features/dashboard/dashgrid/AlertTab.tsx index 5ccfa791328..127e6cd0239 100644 --- a/public/app/features/dashboard/dashgrid/AlertTab.tsx +++ b/public/app/features/dashboard/dashgrid/AlertTab.tsx @@ -2,12 +2,15 @@ import React, { PureComponent } from 'react'; import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader'; import { EditorTabBody, EditorToolbarView, ToolbarButtonType } from './EditorTabBody'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; +import StateHistory from './StateHistory'; import appEvents from 'app/core/app_events'; import { PanelModel } from '../panel_model'; import 'app/features/alerting/AlertTabCtrl'; +import { DashboardModel } from '../dashboard_model'; interface Props { angularPanel?: AngularComponent; + dashboard: DashboardModel; panel: PanelModel; } @@ -70,7 +73,7 @@ export class AlertTab extends PureComponent { return { title: 'State history', render: () => { - return
State history
; + return ; }, buttonType: ToolbarButtonType.View, }; @@ -119,7 +122,6 @@ export class AlertTab extends PureComponent { buttonTitle: 'Create Alert', }; - //TODO move add button react from angular and add condition to render angular view return ( <> diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index b3d94f43487..fbc683c2eb3 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -54,7 +54,7 @@ export class PanelEditor extends PureComponent { case 'queries': return ; case 'alert': - return ; + return ; case 'visualization': return ( { + state = { + stateHistoryItems: [], + }; + + componentDidMount(): void { + const { dashboard, panelId } = this.props; + + getBackendSrv() + .get(`/api/annotations?dashboardId=${dashboard.id}&panelId=${panelId}&limit=50&type=alert`) + .then(res => { + console.log(res); + const items = []; + res.map(item => { + items.push({ + stateModel: alertDef.getStateDisplayModel(item.newState), + time: dashboard.formatDate(item.time, 'MMM D, YYYY HH:mm:ss'), + info: alertDef.getAlertAnnotationInfo(item), + }); + }); + + this.setState({ + stateHistoryItems: items, + }); + }); + } + + render() { + const { stateHistoryItems } = this.state; + + return ( +
+
+
+ Last 50 state changes + +
+
    + {stateHistoryItems ? ( + stateHistoryItems.map((item, index) => { + return ( +
  1. +
    + +
    +
    +
    +

    {item.alertName}

    +
    + {item.stateModel.text} +
    +
    + {item.info} +
    +
    {item.time}
    +
  2. + ); + }) + ) : ( + No state changes recorded + )} +
+
+
+ ); + } +} + +export default StateHistory; From 6b5f9d58217be26761675509ebfa02e0e5ff994f Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Wed, 2 Jan 2019 16:07:29 +0100 Subject: [PATCH 12/68] clear history --- .../features/dashboard/dashgrid/AlertTab.tsx | 12 +++-- .../dashboard/dashgrid/StateHistory.tsx | 53 ++++++++++++++----- 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/AlertTab.tsx b/public/app/features/dashboard/dashgrid/AlertTab.tsx index 127e6cd0239..80706bf9b41 100644 --- a/public/app/features/dashboard/dashgrid/AlertTab.tsx +++ b/public/app/features/dashboard/dashgrid/AlertTab.tsx @@ -19,10 +19,6 @@ export class AlertTab extends PureComponent { component: AngularComponent; panelCtrl: any; - constructor(props) { - super(props); - } - componentDidMount() { if (this.shouldLoadAlertTab()) { this.loadAlertTab(); @@ -73,7 +69,13 @@ export class AlertTab extends PureComponent { return { title: 'State history', render: () => { - return ; + return ( + + ); }, buttonType: ToolbarButtonType.View, }; diff --git a/public/app/features/dashboard/dashgrid/StateHistory.tsx b/public/app/features/dashboard/dashgrid/StateHistory.tsx index d40afdd375a..c2998190841 100644 --- a/public/app/features/dashboard/dashgrid/StateHistory.tsx +++ b/public/app/features/dashboard/dashgrid/StateHistory.tsx @@ -1,11 +1,13 @@ import React, { PureComponent } from 'react'; import alertDef from '../../alerting/state/alertDef'; -import { getBackendSrv } from '../../../core/services/backend_srv'; +import { getBackendSrv } from 'app/core/services/backend_srv'; import { DashboardModel } from '../dashboard_model'; +import appEvents from '../../../core/app_events'; interface Props { dashboard: DashboardModel; panelId: number; + onRefresh: () => void; } interface State { @@ -23,14 +25,12 @@ class StateHistory extends PureComponent { getBackendSrv() .get(`/api/annotations?dashboardId=${dashboard.id}&panelId=${panelId}&limit=50&type=alert`) .then(res => { - console.log(res); - const items = []; - res.map(item => { - items.push({ + const items = res.map(item => { + return { stateModel: alertDef.getStateDisplayModel(item.newState), time: dashboard.formatDate(item.time, 'MMM D, YYYY HH:mm:ss'), info: alertDef.getAlertAnnotationInfo(item), - }); + }; }); this.setState({ @@ -39,20 +39,47 @@ class StateHistory extends PureComponent { }); } + clearHistory = () => { + const { dashboard, onRefresh, panelId } = this.props; + + appEvents.emit('confirm-modal', { + title: 'Delete Alert History', + text: 'Are you sure you want to remove all history & annotations for this alert?', + icon: 'fa-trash', + yesText: 'Yes', + onConfirm: () => { + getBackendSrv() + .post('/api/annotations/mass-delete', { + dashboardId: dashboard.id, + panelId: panelId, + }) + .then(() => { + onRefresh(); + }); + + this.setState({ + stateHistoryItems: [], + }); + }, + }); + }; + render() { const { stateHistoryItems } = this.state; return (
-
- Last 50 state changes - -
+ {stateHistoryItems.length > 0 && ( +
+ Last 50 state changes + +
+ )}
    - {stateHistoryItems ? ( + {stateHistoryItems.length > 0 ? ( stateHistoryItems.map((item, index) => { return (
  1. From 80e441f48b3b65d1125bb3b6f14016918e8b3eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 2 Jan 2019 19:17:35 +0100 Subject: [PATCH 13/68] Fixed new gotmetalinter warning --- pkg/services/dashboards/dashboard_service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/services/dashboards/dashboard_service.go b/pkg/services/dashboards/dashboard_service.go index 7e334ff656f..d9ff1216966 100644 --- a/pkg/services/dashboards/dashboard_service.go +++ b/pkg/services/dashboards/dashboard_service.go @@ -76,7 +76,7 @@ func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO, return nil, models.ErrDashboardFolderCannotHaveParent } - if dash.IsFolder && strings.ToLower(dash.Title) == strings.ToLower(models.RootFolderName) { + if dash.IsFolder && strings.EqualFold(dash.Title, models.RootFolderName) { return nil, models.ErrDashboardFolderNameExists } From 4c51e8df201ce2a3860bb5f94a9c02653a3ea5a3 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Wed, 2 Jan 2019 23:38:09 +0100 Subject: [PATCH 14/68] Don't cut off subsecond precision for postgres macros Change time format to RFC3339Nano for $__timeFilter, $__timeFrom and $__timeTo macros --- pkg/tsdb/postgres/macros.go | 6 +++--- pkg/tsdb/postgres/macros_test.go | 23 ++++++++++++++++++++--- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/pkg/tsdb/postgres/macros.go b/pkg/tsdb/postgres/macros.go index 26a3d1e53ee..0b3685c2610 100644 --- a/pkg/tsdb/postgres/macros.go +++ b/pkg/tsdb/postgres/macros.go @@ -86,11 +86,11 @@ func (m *postgresMacroEngine) evaluateMacro(name string, args []string) (string, return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s BETWEEN '%s' AND '%s'", args[0], m.timeRange.GetFromAsTimeUTC().Format(time.RFC3339), m.timeRange.GetToAsTimeUTC().Format(time.RFC3339)), nil + return fmt.Sprintf("%s BETWEEN '%s' AND '%s'", args[0], m.timeRange.GetFromAsTimeUTC().Format(time.RFC3339Nano), m.timeRange.GetToAsTimeUTC().Format(time.RFC3339Nano)), nil case "__timeFrom": - return fmt.Sprintf("'%s'", m.timeRange.GetFromAsTimeUTC().Format(time.RFC3339)), nil + return fmt.Sprintf("'%s'", m.timeRange.GetFromAsTimeUTC().Format(time.RFC3339Nano)), nil case "__timeTo": - return fmt.Sprintf("'%s'", m.timeRange.GetToAsTimeUTC().Format(time.RFC3339)), nil + return fmt.Sprintf("'%s'", m.timeRange.GetToAsTimeUTC().Format(time.RFC3339Nano)), nil case "__timeGroup": if len(args) < 2 { return "", fmt.Errorf("macro %v needs time column and interval and optional fill value", name) diff --git a/pkg/tsdb/postgres/macros_test.go b/pkg/tsdb/postgres/macros_test.go index 6a71caedeb9..90f6c6d879a 100644 --- a/pkg/tsdb/postgres/macros_test.go +++ b/pkg/tsdb/postgres/macros_test.go @@ -41,7 +41,7 @@ func TestMacroEngine(t *testing.T) { sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", from.Format(time.RFC3339), to.Format(time.RFC3339))) + So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", from.Format(time.RFC3339Nano), to.Format(time.RFC3339Nano))) }) Convey("interpolate __timeFrom function", func() { @@ -138,7 +138,7 @@ func TestMacroEngine(t *testing.T) { sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", from.Format(time.RFC3339), to.Format(time.RFC3339))) + So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", from.Format(time.RFC3339Nano), to.Format(time.RFC3339Nano))) }) Convey("interpolate __unixEpochFilter function", func() { @@ -158,7 +158,7 @@ func TestMacroEngine(t *testing.T) { sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", from.Format(time.RFC3339), to.Format(time.RFC3339))) + So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", from.Format(time.RFC3339Nano), to.Format(time.RFC3339Nano))) }) Convey("interpolate __unixEpochFilter function", func() { @@ -168,5 +168,22 @@ func TestMacroEngine(t *testing.T) { So(sql, ShouldEqual, fmt.Sprintf("select time >= %d AND time <= %d", from.Unix(), to.Unix())) }) }) + + Convey("Given a time range between 1960-02-01 07:00:00.5 and 1980-02-03 08:00:00.5", func() { + from := time.Date(1960, 2, 1, 7, 0, 0, 500e6, time.UTC) + to := time.Date(1980, 2, 3, 8, 0, 0, 500e6, time.UTC) + timeRange := tsdb.NewTimeRange(strconv.FormatInt(from.UnixNano()/int64(time.Millisecond), 10), strconv.FormatInt(to.UnixNano()/int64(time.Millisecond), 10)) + + So(from.Format(time.RFC3339Nano), ShouldEqual, "1960-02-01T07:00:00.5Z") + So(to.Format(time.RFC3339Nano), ShouldEqual, "1980-02-03T08:00:00.5Z") + Convey("interpolate __timeFilter function", func() { + sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", from.Format(time.RFC3339Nano), to.Format(time.RFC3339Nano))) + }) + + }) + }) } From 4892d3f54be27014560ed55b72b9845cdf64d16d Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Thu, 3 Jan 2019 14:21:58 +0100 Subject: [PATCH 15/68] Fixing issue with value color being wrong --- public/app/viz/Gauge.test.tsx | 55 +++++++++++++++++++++++++++++++++++ public/app/viz/Gauge.tsx | 14 +++++---- 2 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 public/app/viz/Gauge.test.tsx diff --git a/public/app/viz/Gauge.test.tsx b/public/app/viz/Gauge.test.tsx new file mode 100644 index 00000000000..91107a563e5 --- /dev/null +++ b/public/app/viz/Gauge.test.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import { Gauge, Props } from './Gauge'; +import { BasicGaugeColor } from '../types'; +import { TimeSeriesVMs } from '@grafana/ui'; + +jest.mock('jquery', () => ({ + plot: jest.fn(), +})); + +const setup = (propOverrides?: object) => { + const props: Props = { + baseColor: BasicGaugeColor.Green, + maxValue: 100, + mappings: [], + minValue: 0, + prefix: '', + showThresholdMarkers: true, + showThresholdLabels: false, + suffix: '', + thresholds: [], + unit: 'none', + stat: 'avg', + height: 300, + width: 300, + timeSeries: {} as TimeSeriesVMs, + decimals: 0, + }; + + Object.assign(props, propOverrides); + + const wrapper = shallow(); + const instance = wrapper.instance() as Gauge; + + return { + instance, + wrapper, + }; +}; + +describe('Get font color', () => { + it('should get base color if no threshold', () => { + const { instance } = setup(); + + expect(instance.getFontColor(40)).toEqual(BasicGaugeColor.Green); + }); + + it('should be f2f2f2', () => { + const { instance } = setup({ + thresholds: [{ value: 59, color: '#f2f2f2' }], + }); + + expect(instance.getFontColor(58)).toEqual('#f2f2f2'); + }); +}); diff --git a/public/app/viz/Gauge.tsx b/public/app/viz/Gauge.tsx index 031d856f492..defeaf8cc8f 100644 --- a/public/app/viz/Gauge.tsx +++ b/public/app/viz/Gauge.tsx @@ -5,7 +5,7 @@ import { TimeSeriesVMs } from '@grafana/ui'; import config from '../core/config'; import kbn from '../core/utils/kbn'; -interface Props { +export interface Props { baseColor: string; decimals: number; height: number; @@ -96,12 +96,14 @@ export class Gauge extends PureComponent { getFontColor(value) { const { baseColor, maxValue, thresholds } = this.props; - const atThreshold = thresholds.filter(threshold => value <= threshold.value); + if (thresholds.length > 0) { + const atThreshold = thresholds.filter(threshold => value <= threshold.value); - if (atThreshold.length > 0) { - return atThreshold[0].color; - } else if (value <= maxValue) { - return BasicGaugeColor.Red; + if (atThreshold.length > 0) { + return atThreshold[0].color; + } else if (value <= maxValue) { + return BasicGaugeColor.Red; + } } return baseColor; From 44e2fd4b226e8e3000eba9c9485e57d4270f3de0 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Thu, 3 Jan 2019 14:31:45 +0100 Subject: [PATCH 16/68] Fix issue with value disappearing when selecting stat --- public/app/plugins/panel/gauge/module.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/panel/gauge/module.tsx b/public/app/plugins/panel/gauge/module.tsx index 245a17abe52..dccd424b416 100644 --- a/public/app/plugins/panel/gauge/module.tsx +++ b/public/app/plugins/panel/gauge/module.tsx @@ -38,8 +38,8 @@ export const defaultProps = { showThresholdLabels: false, suffix: '', decimals: 0, - stat: '', - unit: '', + stat: 'avg', + unit: 'none', mappings: [], thresholds: [], }, From 297241c4aba4c6db70181b7974f376a1b3a609b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 4 Jan 2019 09:35:16 +0100 Subject: [PATCH 17/68] Fixed timepicker css issue introduced by PR #14700 --- public/sass/components/_timepicker.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/sass/components/_timepicker.scss b/public/sass/components/_timepicker.scss index f424e42ded5..e4cb5ce9c21 100644 --- a/public/sass/components/_timepicker.scss +++ b/public/sass/components/_timepicker.scss @@ -20,9 +20,9 @@ display: flex; flex-direction: column; position: absolute; - left: 20px; right: 20px; top: $navbarHeight; + @include media-breakpoint-up(md) { width: 550px; } From 0ca0670e0039723f9beef4e77264370b42cf2851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 4 Jan 2019 09:58:24 +0100 Subject: [PATCH 18/68] Changed datasource list page default layout mode --- public/app/features/datasources/state/reducers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/features/datasources/state/reducers.ts b/public/app/features/datasources/state/reducers.ts index e8625aac0d2..6e86c304fa7 100644 --- a/public/app/features/datasources/state/reducers.ts +++ b/public/app/features/datasources/state/reducers.ts @@ -5,7 +5,7 @@ import { LayoutModes } from '../../../core/components/LayoutSelector/LayoutSelec const initialState: DataSourcesState = { dataSources: [] as DataSource[], dataSource: {} as DataSource, - layoutMode: LayoutModes.Grid, + layoutMode: LayoutModes.List, searchQuery: '', dataSourcesCount: 0, dataSourceTypes: [] as Plugin[], From 2eeba9dae186806ed5977d1e79815bfc5328169c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 4 Jan 2019 11:57:16 +0100 Subject: [PATCH 19/68] Updated alert tab layout & markup --- public/app/features/alerting/AlertTabCtrl.ts | 3 +- .../features/alerting/partials/alert_tab.html | 217 ++++++++++-------- 2 files changed, 117 insertions(+), 103 deletions(-) diff --git a/public/app/features/alerting/AlertTabCtrl.ts b/public/app/features/alerting/AlertTabCtrl.ts index ffcb20472ad..2be25e9df6a 100644 --- a/public/app/features/alerting/AlertTabCtrl.ts +++ b/public/app/features/alerting/AlertTabCtrl.ts @@ -115,7 +115,7 @@ export class AlertTabCtrl { } getNotifications() { - return Promise.resolve( + return this.$q.when( this.notifications.map(item => { return this.uiSegmentSrv.newSegment(item.name); }) @@ -148,6 +148,7 @@ export class AlertTabCtrl { // reset plus button this.addNotificationSegment.value = this.uiSegmentSrv.newPlusButton().value; this.addNotificationSegment.html = this.uiSegmentSrv.newPlusButton().html; + this.addNotificationSegment.fake = true; } removeNotification(index) { diff --git a/public/app/features/alerting/partials/alert_tab.html b/public/app/features/alerting/partials/alert_tab.html index 3a03b23a076..da862203da6 100644 --- a/public/app/features/alerting/partials/alert_tab.html +++ b/public/app/features/alerting/partials/alert_tab.html @@ -4,112 +4,121 @@
-
-
+
+

Rule

+
Name
-
-
- Evaluate every - -
-
- - - - If an alert rule has a configured For and the query violates the configured - threshold it - will first go from OK to Pending. - Going from OK to Pending Grafana will not send any notifications. Once the alert - rule - has - been firing for more than For duration, it will change to Alerting and send alert - notifications. - -
+
+ Evaluate every + +
+
+ + + + If an alert rule has a configured For and the query violates the configured + threshold it + will first go from OK to Pending. + Going from OK to Pending Grafana will not send any notifications. Once the alert + rule + has + been firing for more than For duration, it will change to Alerting and send alert + notifications. +
-
-
-
-
Conditions
-
-
-
- - WHEN -
-
- - - OF -
-
- - -
-
- - - - -
-
- -
-
-
- -
+
-
- If no data or all values are null - SET STATE TO -
- +

Conditions

+
+
+ + WHEN +
+
+ + + OF +
+
+ + +
+
+ + + + +
+
+
- If execution error or timeout - SET STATE TO -
- + +
+
+ +
+

No Data & Error Handling

+
+
+ If no data or all values are null +
+
+ SET STATE TO +
+ +
+
+
+ +
+
+ If execution error or timeout +
+
+ SET STATE TO +
+ +
@@ -129,19 +138,23 @@
+
Notifications
-
+
Send to - -  {{nc.name}}  - - - +
+ +  {{nc.name}}  + + +
+
+
From f749ced36e05956e02ed0574a24e8350b64e5b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 4 Jan 2019 12:18:49 +0100 Subject: [PATCH 20/68] AlertTab style fixes --- .../dashboard/dashgrid/EditorTabBody.tsx | 2 +- .../dashboard/dashgrid/StateHistory.tsx | 64 +++++++++---------- public/sass/pages/_alerting.scss | 1 + 3 files changed, 33 insertions(+), 34 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx b/public/app/features/dashboard/dashgrid/EditorTabBody.tsx index 2cd247ed704..822507c5c11 100644 --- a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx +++ b/public/app/features/dashboard/dashgrid/EditorTabBody.tsx @@ -25,7 +25,7 @@ export interface EditorToolbarView { icon?: string; disabled?: boolean; onClick?: () => void; - render?: (closeFunction?: any) => JSX.Element | JSX.Element[]; + render?: () => JSX.Element; action?: () => void; buttonType: ToolbarButtonType; } diff --git a/public/app/features/dashboard/dashgrid/StateHistory.tsx b/public/app/features/dashboard/dashgrid/StateHistory.tsx index c2998190841..99229b41848 100644 --- a/public/app/features/dashboard/dashgrid/StateHistory.tsx +++ b/public/app/features/dashboard/dashgrid/StateHistory.tsx @@ -69,41 +69,39 @@ class StateHistory extends PureComponent { return (
-
- {stateHistoryItems.length > 0 && ( -
- Last 50 state changes - -
- )} -
    - {stateHistoryItems.length > 0 ? ( - stateHistoryItems.map((item, index) => { - return ( -
  1. -
    - -
    -
    -
    -

    {item.alertName}

    -
    - {item.stateModel.text} -
    + {stateHistoryItems.length > 0 && ( +
    + Last 50 state changes + +
    + )} +
      + {stateHistoryItems.length > 0 ? ( + stateHistoryItems.map((item, index) => { + return ( +
    1. +
      + +
      +
      +
      +

      {item.alertName}

      +
      + {item.stateModel.text}
      - {item.info}
      -
      {item.time}
      -
    2. - ); - }) - ) : ( - No state changes recorded - )} -
    -
    + {item.info} +
    +
    {item.time}
    +
  2. + ); + }) + ) : ( + No state changes recorded + )} +
); } diff --git a/public/sass/pages/_alerting.scss b/public/sass/pages/_alerting.scss index 77752be11bc..f285ab753ff 100644 --- a/public/sass/pages/_alerting.scss +++ b/public/sass/pages/_alerting.scss @@ -107,6 +107,7 @@ display: flex; flex-direction: column; flex-grow: 1; + justify-content: center; overflow: hidden; } From 35f6f50f4092b5bef5023e8a499f4cd1605a47bf Mon Sep 17 00:00:00 2001 From: Simon Podlipsky Date: Fri, 4 Jan 2019 12:24:22 +0100 Subject: [PATCH 21/68] Fixed timepicker css issue introduced by PR #14700 and remove hotfix from 297241c --- public/sass/components/_timepicker.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/sass/components/_timepicker.scss b/public/sass/components/_timepicker.scss index e4cb5ce9c21..6f075c4d92e 100644 --- a/public/sass/components/_timepicker.scss +++ b/public/sass/components/_timepicker.scss @@ -20,10 +20,12 @@ display: flex; flex-direction: column; position: absolute; + left: 20px; right: 20px; top: $navbarHeight; @include media-breakpoint-up(md) { + left: auto; width: 550px; } From e0c28ba7704246a70e4467f23109cadacb8f3fc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 4 Jan 2019 12:38:50 +0100 Subject: [PATCH 22/68] Minor refactoring of EditorTabBody --- .../features/dashboard/dashgrid/AlertTab.tsx | 17 +++++++++++------ .../dashboard/dashgrid/EditorTabBody.tsx | 10 ++-------- .../features/dashboard/dashgrid/QueriesTab.tsx | 9 +++++---- .../dashboard/dashgrid/VisualizationTab.tsx | 6 ++++-- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/public/app/features/dashboard/dashgrid/AlertTab.tsx b/public/app/features/dashboard/dashgrid/AlertTab.tsx index 80706bf9b41..20f7e90633e 100644 --- a/public/app/features/dashboard/dashgrid/AlertTab.tsx +++ b/public/app/features/dashboard/dashgrid/AlertTab.tsx @@ -1,12 +1,19 @@ +// Libraries import React, { PureComponent } from 'react'; + +// Services & Utils import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader'; -import { EditorTabBody, EditorToolbarView, ToolbarButtonType } from './EditorTabBody'; +import appEvents from 'app/core/app_events'; + +// Components +import { EditorTabBody, EditorToolbarView } from './EditorTabBody'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import StateHistory from './StateHistory'; -import appEvents from 'app/core/app_events'; -import { PanelModel } from '../panel_model'; import 'app/features/alerting/AlertTabCtrl'; + +// Types import { DashboardModel } from '../dashboard_model'; +import { PanelModel } from '../panel_model'; interface Props { angularPanel?: AngularComponent; @@ -77,7 +84,6 @@ export class AlertTab extends PureComponent { /> ); }, - buttonType: ToolbarButtonType.View, }; }; @@ -85,7 +91,7 @@ export class AlertTab extends PureComponent { const { panel } = this.props; return { title: 'Delete', - icon: 'fa fa-trash', + btnType: 'danger', onClick: () => { appEvents.emit('confirm-modal', { title: 'Delete Alert', @@ -102,7 +108,6 @@ export class AlertTab extends PureComponent { }, }); }, - buttonType: ToolbarButtonType.Action, }; }; diff --git a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx b/public/app/features/dashboard/dashgrid/EditorTabBody.tsx index 822507c5c11..b7da81a23f8 100644 --- a/public/app/features/dashboard/dashgrid/EditorTabBody.tsx +++ b/public/app/features/dashboard/dashgrid/EditorTabBody.tsx @@ -13,21 +13,15 @@ interface Props { toolbarItems?: EditorToolbarView[]; } -export enum ToolbarButtonType { - Action = 'action', - View = 'view', -} - export interface EditorToolbarView { title?: string; heading?: string; - imgSrc?: string; icon?: string; disabled?: boolean; onClick?: () => void; render?: () => JSX.Element; action?: () => void; - buttonType: ToolbarButtonType; + btnType?: 'danger'; } interface State { @@ -87,7 +81,7 @@ export class EditorTabBody extends PureComponent { view.onClick(); } - if (view.buttonType !== ToolbarButtonType.Action) { + if (view.render) { this.onToggleToolBarView(view); } }; diff --git a/public/app/features/dashboard/dashgrid/QueriesTab.tsx b/public/app/features/dashboard/dashgrid/QueriesTab.tsx index 98287e4888e..741e2cd9ac1 100644 --- a/public/app/features/dashboard/dashgrid/QueriesTab.tsx +++ b/public/app/features/dashboard/dashgrid/QueriesTab.tsx @@ -1,19 +1,22 @@ // Libraries import React, { PureComponent, SFC } from 'react'; import _ from 'lodash'; + // Components -import './../../panel/metrics_tab'; -import { EditorTabBody, EditorToolbarView, ToolbarButtonType } from './EditorTabBody'; +import 'app/features/panel/metrics_tab'; +import { EditorTabBody, EditorToolbarView} from './EditorTabBody'; import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker'; import { QueryInspector } from './QueryInspector'; import { QueryOptions } from './QueryOptions'; import { AngularQueryComponentScope } from 'app/features/panel/metrics_tab'; import { PanelOptionSection } from './PanelOptionSection'; + // Services import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { BackendSrv, getBackendSrv } from 'app/core/services/backend_srv'; import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader'; import config from 'app/core/config'; + // Types import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; @@ -204,14 +207,12 @@ export class QueriesTab extends PureComponent { const queryInspector: EditorToolbarView = { title: 'Query Inspector', render: this.renderQueryInspector, - buttonType: ToolbarButtonType.View, }; const dsHelp: EditorToolbarView = { heading: 'Help', icon: 'fa fa-question', render: this.renderHelp, - buttonType: ToolbarButtonType.View, }; return ( diff --git a/public/app/features/dashboard/dashgrid/VisualizationTab.tsx b/public/app/features/dashboard/dashgrid/VisualizationTab.tsx index 8a57dd17190..bc7102f35dd 100644 --- a/public/app/features/dashboard/dashgrid/VisualizationTab.tsx +++ b/public/app/features/dashboard/dashgrid/VisualizationTab.tsx @@ -1,13 +1,16 @@ // Libraries import React, { PureComponent } from 'react'; + // Utils & Services import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader'; + // Components -import { EditorTabBody, EditorToolbarView, ToolbarButtonType } from './EditorTabBody'; +import { EditorTabBody, EditorToolbarView } from './EditorTabBody'; import { VizTypePicker } from './VizTypePicker'; import { PluginHelp } from 'app/core/components/PluginHelp/PluginHelp'; import { FadeIn } from 'app/core/components/Animations/FadeIn'; import { PanelOptionSection } from './PanelOptionSection'; + // Types import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; @@ -207,7 +210,6 @@ export class VisualizationTab extends PureComponent { heading: 'Help', icon: 'fa fa-question', render: this.renderHelp, - buttonType: ToolbarButtonType.View, }; return ( From dd6f606cda65284a3d0ebf8ca3c700ea5f86f072 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Fri, 4 Jan 2019 16:30:19 +0100 Subject: [PATCH 23/68] docs: updated debian and centos repo. --- docs/sources/installation/debian.md | 21 ++++++--------------- docs/sources/installation/rpm.md | 16 +++++----------- 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/docs/sources/installation/debian.md b/docs/sources/installation/debian.md index 7ed44572533..28b975e31e8 100644 --- a/docs/sources/installation/debian.md +++ b/docs/sources/installation/debian.md @@ -34,32 +34,23 @@ sudo dpkg -i grafana__amd64.deb Example: ```bash -wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_5.1.4_amd64.deb +wget https://dl.grafana.com/oss/release/grafana_5.4.2_amd64.deb sudo apt-get install -y adduser libfontconfig -sudo dpkg -i grafana_5.1.4_amd64.deb +sudo dpkg -i grafana_5.4.2_amd64.deb ``` ## APT Repository -Add the following line to your `/etc/apt/sources.list` file. +Create a file `/etc/apt/sources.list.d/grafana.list` and add the following to it. ```bash -deb https://packagecloud.io/grafana/stable/debian/ stretch main +deb https://packages.grafana.com/oss/deb stable main ``` -Use the above line even if you are on Ubuntu or another Debian version. -There is also a testing repository if you want beta or release -candidates. +Use the above line even if you are on Ubuntu or another Debian version. Then add our gpg key. This allows you to install signed packages. ```bash -deb https://packagecloud.io/grafana/testing/debian/ stretch main -``` - -Then add the [Package Cloud](https://packagecloud.io/grafana) key. This -allows you to install signed packages. - -```bash -curl https://packagecloud.io/gpg.key | sudo apt-key add - +curl https://packages.grafana.com/gpg.key | sudo apt-key add - ``` Update your Apt repositories and install Grafana diff --git a/docs/sources/installation/rpm.md b/docs/sources/installation/rpm.md index 5bf3b7ed745..559bb0a8ef4 100644 --- a/docs/sources/installation/rpm.md +++ b/docs/sources/installation/rpm.md @@ -32,7 +32,7 @@ $ sudo yum install Example: ```bash -$ sudo yum install https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.1.4-1.x86_64.rpm +$ sudo yum install https://dl.grafana.com/oss/release/grafana-5.4.2-1.x86_64.rpm ``` Or install manually using `rpm`. First execute @@ -44,7 +44,7 @@ $ wget Example: ```bash -$ wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.1.4-1.x86_64.rpm +$ wget https://dl.grafana.com/oss/release/grafana-5.4.2-1.x86_64.rpm ``` ### On CentOS / Fedora / Redhat: @@ -67,21 +67,15 @@ Add the following to a new file at `/etc/yum.repos.d/grafana.repo` ```bash [grafana] name=grafana -baseurl=https://packagecloud.io/grafana/stable/el/7/$basearch +baseurl=https://packages.grafana.com/oss/rpm repo_gpgcheck=1 enabled=1 gpgcheck=1 -gpgkey=https://packagecloud.io/gpg.key https://grafanarel.s3.amazonaws.com/RPM-GPG-KEY-grafana +gpgkey=https://packages.grafana.com/gpg.key sslverify=1 sslcacert=/etc/pki/tls/certs/ca-bundle.crt ``` -There is also a testing repository if you want beta or release candidates. - -```bash -baseurl=https://packagecloud.io/grafana/testing/el/7/$basearch -``` - Then install Grafana via the `yum` command. ```bash @@ -91,7 +85,7 @@ $ sudo yum install grafana ### RPM GPG Key The RPMs are signed, you can verify the signature with this [public GPG -key](https://grafanarel.s3.amazonaws.com/RPM-GPG-KEY-grafana). +key](https://packages.grafana.com/gpg.key). ## Package details From c39dc1fb1502a7a4a53570e91a00d48e762843b1 Mon Sep 17 00:00:00 2001 From: Simon Podlipsky Date: Sat, 5 Jan 2019 15:19:54 +0100 Subject: [PATCH 24/68] Some cleanup --- public/app/features/datasources/settings/BasicSettings.tsx | 2 +- .../settings/__snapshots__/BasicSettings.test.tsx.snap | 2 +- public/app/features/explore/LogLabels.tsx | 1 - public/app/features/explore/Typeahead.tsx | 2 +- public/app/features/panel/metrics_tab.ts | 3 --- public/app/plugins/datasource/influxdb/datasource.ts | 2 +- .../plugins/datasource/loki/components/LokiQueryField.tsx | 1 - public/app/plugins/datasource/stackdriver/datasource.ts | 1 - public/app/plugins/datasource/stackdriver/query_ctrl.ts | 1 - public/app/plugins/panel/heatmap/rendering.ts | 7 ++----- public/emails/invited_to_org.html | 2 +- public/emails/new_user_invite.html | 2 +- scripts/webpack/webpack.common.js | 4 +--- scripts/webpack/webpack.prod.js | 1 - 14 files changed, 9 insertions(+), 22 deletions(-) diff --git a/public/app/features/datasources/settings/BasicSettings.tsx b/public/app/features/datasources/settings/BasicSettings.tsx index 569e0909c4d..120e002ac68 100644 --- a/public/app/features/datasources/settings/BasicSettings.tsx +++ b/public/app/features/datasources/settings/BasicSettings.tsx @@ -16,7 +16,7 @@ const BasicSettings: SFC = ({ dataSourceName, isDefault, onDefaultChange,
- +
- +
-
\ No newline at end of file +
diff --git a/public/app/plugins/datasource/stackdriver/partials/query.editor.html b/public/app/plugins/datasource/stackdriver/partials/query.editor.html index 66961d106aa..8760aaf1bb3 100755 --- a/public/app/plugins/datasource/stackdriver/partials/query.editor.html +++ b/public/app/plugins/datasource/stackdriver/partials/query.editor.html @@ -1,5 +1,6 @@ - +
diff --git a/public/app/plugins/datasource/stackdriver/query_aggregation_ctrl.ts b/public/app/plugins/datasource/stackdriver/query_aggregation_ctrl.ts index 628cc494242..f5ff3ad3143 100644 --- a/public/app/plugins/datasource/stackdriver/query_aggregation_ctrl.ts +++ b/public/app/plugins/datasource/stackdriver/query_aggregation_ctrl.ts @@ -1,6 +1,6 @@ import coreModule from 'app/core/core_module'; import _ from 'lodash'; -import * as options from './constants'; +import { alignmentPeriods } from './constants'; import { getAlignmentOptionsByMetric, getAggregationOptionsByMetric } from './functions'; import kbn from 'app/core/utils/kbn'; @@ -29,9 +29,16 @@ export class StackdriverAggregationCtrl { constructor(private $scope, private templateSrv) { this.$scope.ctrl = this; this.target = $scope.target; - this.alignmentPeriods = options.alignmentPeriods; - this.aggOptions = options.aggOptions; - this.alignOptions = options.alignOptions; + this.alignmentPeriods = [ + this.getTemplateVariablesGroup(), + { + label: 'Alignment Periods', + options: alignmentPeriods.map(ap => ({ + ...ap, + label: ap.text, + })), + }, + ]; this.setAggOptions(); this.setAlignOptions(); const self = this; @@ -39,30 +46,68 @@ export class StackdriverAggregationCtrl { self.setAggOptions(); self.setAlignOptions(); }); + this.handleAlignmentChange = this.handleAlignmentChange.bind(this); + this.handleAggregationChange = this.handleAggregationChange.bind(this); + this.handleAlignmentPeriodChange = this.handleAlignmentPeriodChange.bind(this); } setAlignOptions() { - this.alignOptions = getAlignmentOptionsByMetric(this.target.valueType, this.target.metricKind); - if (!this.alignOptions.find(o => o.value === this.templateSrv.replace(this.target.aggregation.perSeriesAligner))) { - this.target.aggregation.perSeriesAligner = this.alignOptions.length > 0 ? this.alignOptions[0].value : ''; + const alignments = getAlignmentOptionsByMetric(this.target.valueType, this.target.metricKind).map(a => ({ + ...a, + label: a.text, + })); + this.alignOptions = [ + this.getTemplateVariablesGroup(), + { + label: 'Alignment Options', + options: alignments, + }, + ]; + if (!alignments.find(o => o.value === this.templateSrv.replace(this.target.aggregation.perSeriesAligner))) { + this.target.aggregation.perSeriesAligner = alignments.length > 0 ? alignments[0].value : ''; } } setAggOptions() { - this.aggOptions = getAggregationOptionsByMetric(this.target.valueType, this.target.metricKind); - - if (!this.aggOptions.find(o => o.value === this.templateSrv.replace(this.target.aggregation.crossSeriesReducer))) { + let aggregations = getAggregationOptionsByMetric(this.target.valueType, this.target.metricKind).map(a => ({ + ...a, + label: a.text, + })); + if (!aggregations.find(o => o.value === this.templateSrv.replace(this.target.aggregation.crossSeriesReducer))) { this.deselectAggregationOption('REDUCE_NONE'); } if (this.target.aggregation.groupBys.length > 0) { - this.aggOptions = this.aggOptions.filter(o => o.value !== 'REDUCE_NONE'); + aggregations = this.aggOptions.filter(o => o.value !== 'REDUCE_NONE'); this.deselectAggregationOption('REDUCE_NONE'); } + this.aggOptions = [ + this.getTemplateVariablesGroup(), + { + label: 'Aggregations', + options: aggregations, + }, + ]; + } + + handleAlignmentChange(value) { + this.target.aggregation.perSeriesAligner = value; + this.$scope.refresh(); + } + + handleAggregationChange(value) { + this.target.aggregation.crossSeriesReducer = value; + this.$scope.refresh(); + } + + handleAlignmentPeriodChange(value) { + this.target.aggregation.alignmentPeriod = value; + this.$scope.refresh(); } formatAlignmentText() { - const selectedAlignment = this.alignOptions.find( + const alignments = getAlignmentOptionsByMetric(this.target.valueType, this.target.metricKind); + const selectedAlignment = alignments.find( ap => ap.value === this.templateSrv.replace(this.target.aggregation.perSeriesAligner) ); return `${kbn.secondsToHms(this.$scope.alignmentPeriod)} interval (${ @@ -74,6 +119,16 @@ export class StackdriverAggregationCtrl { const newValue = this.aggOptions.find(o => o.value !== notValidOptionValue); this.target.aggregation.crossSeriesReducer = newValue ? newValue.value : ''; } + + getTemplateVariablesGroup() { + return { + label: 'Template Variables', + options: this.templateSrv.variables.map(v => ({ + label: `$${v.name}`, + value: `$${v.name}`, + })), + }; + } } coreModule.directive('stackdriverAggregation', StackdriverAggregation); From 2bc38b7907d23dc7038cdd04d02dc8185c0d764b Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 12 Dec 2018 12:55:23 +0100 Subject: [PATCH 34/68] remove redundant default value --- .../datasource/stackdriver/components/OptionGroupPicker.tsx | 1 - .../plugins/datasource/stackdriver/components/OptionPicker.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/public/app/plugins/datasource/stackdriver/components/OptionGroupPicker.tsx b/public/app/plugins/datasource/stackdriver/components/OptionGroupPicker.tsx index 7401b226413..f7863dd2a16 100644 --- a/public/app/plugins/datasource/stackdriver/components/OptionGroupPicker.tsx +++ b/public/app/plugins/datasource/stackdriver/components/OptionGroupPicker.tsx @@ -41,7 +41,6 @@ export class OptionGroupPicker extends React.Component { }} styles={ResetStyles} isSearchable={searchable} - maxMenuHeight={50} onChange={option => onChange(option.value)} getOptionValue={i => i.value} getOptionLabel={i => i.label} diff --git a/public/app/plugins/datasource/stackdriver/components/OptionPicker.tsx b/public/app/plugins/datasource/stackdriver/components/OptionPicker.tsx index 3d99ed3bafc..64d68f1bd70 100644 --- a/public/app/plugins/datasource/stackdriver/components/OptionPicker.tsx +++ b/public/app/plugins/datasource/stackdriver/components/OptionPicker.tsx @@ -39,7 +39,6 @@ export class OptionPicker extends React.Component { }} styles={ResetStyles} isSearchable={searchable} - maxMenuHeight={50} onChange={option => onChange(option.value)} getOptionValue={i => i.value} getOptionLabel={i => i.label} From b683a19ff6d6278b11b3b963221f2d224815a466 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 12 Dec 2018 12:57:57 +0100 Subject: [PATCH 35/68] update failing tests --- .../stackdriver/query_aggregation_ctrl.ts | 6 +- .../stackdriver/query_filter_ctrl.ts | 14 +---- .../specs/query_aggregation_ctrl.test.ts | 57 +++++++++++++++++-- 3 files changed, 56 insertions(+), 21 deletions(-) diff --git a/public/app/plugins/datasource/stackdriver/query_aggregation_ctrl.ts b/public/app/plugins/datasource/stackdriver/query_aggregation_ctrl.ts index f5ff3ad3143..d4478c6214c 100644 --- a/public/app/plugins/datasource/stackdriver/query_aggregation_ctrl.ts +++ b/public/app/plugins/datasource/stackdriver/query_aggregation_ctrl.ts @@ -52,6 +52,7 @@ export class StackdriverAggregationCtrl { } setAlignOptions() { + console.log('this.target.metricKind', this.target.metricKind); const alignments = getAlignmentOptionsByMetric(this.target.valueType, this.target.metricKind).map(a => ({ ...a, label: a.text, @@ -78,7 +79,7 @@ export class StackdriverAggregationCtrl { } if (this.target.aggregation.groupBys.length > 0) { - aggregations = this.aggOptions.filter(o => o.value !== 'REDUCE_NONE'); + aggregations = aggregations.filter(o => o.value !== 'REDUCE_NONE'); this.deselectAggregationOption('REDUCE_NONE'); } this.aggOptions = [ @@ -116,7 +117,8 @@ export class StackdriverAggregationCtrl { } deselectAggregationOption(notValidOptionValue: string) { - const newValue = this.aggOptions.find(o => o.value !== notValidOptionValue); + const aggregations = getAggregationOptionsByMetric(this.target.valueType, this.target.metricKind); + const newValue = aggregations.find(o => o.value !== notValidOptionValue); this.target.aggregation.crossSeriesReducer = newValue ? newValue.value : ''; } diff --git a/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts b/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts index ba96cfc5a60..74ca2c9b067 100644 --- a/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts +++ b/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts @@ -160,23 +160,11 @@ export class StackdriverFilterCtrl { options: this.templateSrv.variables.map(v => ({ label: `$${v.name}`, value: `$${v.name}`, - description: `$${v.definition}`, + // description: `$${v.definition}`, })), }; } - insertTemplateVariables(options) { - const templateVariables = { - label: 'Template Variables', - options: this.templateSrv.variables.map(v => ({ - label: `$${v.name}`, - value: `$${v.name}`, - description: `$${v.definition}`, - })), - }; - return [templateVariables, { label: 'Metrics', options }]; - } - getMetricsList() { const metrics = this.metricDescriptors.map(m => { return { diff --git a/public/app/plugins/datasource/stackdriver/specs/query_aggregation_ctrl.test.ts b/public/app/plugins/datasource/stackdriver/specs/query_aggregation_ctrl.test.ts index 81011f5dfe0..6e83824d504 100644 --- a/public/app/plugins/datasource/stackdriver/specs/query_aggregation_ctrl.test.ts +++ b/public/app/plugins/datasource/stackdriver/specs/query_aggregation_ctrl.test.ts @@ -17,27 +17,68 @@ describe('StackdriverAggregationCtrl', () => { }, { replace: s => s, + variables: [{ name: 'someVariable1' }, { name: 'someVariable2' }], } ); }); it('should populate all aggregate options except two', () => { ctrl.setAggOptions(); - expect(ctrl.aggOptions.length).toBe(11); - expect(ctrl.aggOptions.map(o => o.value)).toEqual( + expect(ctrl.aggOptions.length).toBe(2); + const [templateVariableGroup, aggOptionsGroup] = ctrl.aggOptions; + expect(templateVariableGroup.options.length).toBe(2); + expect(aggOptionsGroup.options.length).toBe(11); + expect(aggOptionsGroup.options.map(o => o.value)).toEqual( expect['not'].arrayContaining(['REDUCE_COUNT_TRUE', 'REDUCE_COUNT_FALSE']) ); }); it('should populate all alignment options except two', () => { ctrl.setAlignOptions(); - expect(ctrl.alignOptions.length).toBe(9); - expect(ctrl.alignOptions.map(o => o.value)).toEqual( + const [templateVariableGroup, alignOptionGroup] = ctrl.aggOptions; + expect(templateVariableGroup.options.length).toBe(2); + expect(alignOptionGroup.options.length).toBe(11); + expect(alignOptionGroup.options.map(o => o.value)).toEqual( expect['not'].arrayContaining(['REDUCE_COUNT_TRUE', 'REDUCE_COUNT_FALSE']) ); }); }); + describe('and result is double and delta and no group by is used', () => { + beforeEach(async () => { + ctrl = new StackdriverAggregationCtrl( + { + $on: () => {}, + target: { + valueType: 'DOUBLE', + metricKind: 'DELTA', + aggregation: { crossSeriesReducer: '', groupBys: [] }, + }, + }, + { + replace: s => s, + variables: [{ name: 'someVariable1' }, { name: 'someVariable2' }], + } + ); + }); + + it('should populate all alignment options except four', () => { + ctrl.setAlignOptions(); + const [templateVariableGroup, alignOptionGroup] = ctrl.alignOptions; + expect(templateVariableGroup.options.length).toBe(2); + expect(alignOptionGroup.options.length).toBe(9); + expect(alignOptionGroup.options.map(o => o.value)).toEqual( + expect['not'].arrayContaining([ + 'ALIGN_NEXT_OLDER', + 'ALIGN_INTERPOLATE', + 'ALIGN_COUNT_TRUE', + 'ALIGN_COUNT_FALSE', + 'ALIGN_FRACTION_TRUE', + ]) + ); + }); + }); + describe('and result is double and gauge and a group by is used', () => { beforeEach(async () => { ctrl = new StackdriverAggregationCtrl( @@ -51,14 +92,18 @@ describe('StackdriverAggregationCtrl', () => { }, { replace: s => s, + variables: [{ name: 'someVariable1' }], } ); }); it('should populate all aggregate options except three', () => { ctrl.setAggOptions(); - expect(ctrl.aggOptions.length).toBe(10); - expect(ctrl.aggOptions.map(o => o.value)).toEqual( + const [templateVariableGroup, aggOptionsGroup] = ctrl.aggOptions; + expect(ctrl.aggOptions.length).toBe(2); + expect(templateVariableGroup.options.length).toBe(1); + expect(aggOptionsGroup.options.length).toBe(10); + expect(aggOptionsGroup.options.map(o => o.value)).toEqual( expect['not'].arrayContaining(['REDUCE_COUNT_TRUE', 'REDUCE_COUNT_FALSE', 'REDUCE_NONE']) ); }); From 329d4652fb3004f4c51ae36c029ea04aa9c3d553 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 12 Dec 2018 13:57:32 +0100 Subject: [PATCH 36/68] cleanup query filter --- .../stackdriver/partials/query.filter.html | 2 +- .../stackdriver/query_filter_ctrl.ts | 62 +++++-------------- 2 files changed, 15 insertions(+), 49 deletions(-) diff --git a/public/app/plugins/datasource/stackdriver/partials/query.filter.html b/public/app/plugins/datasource/stackdriver/partials/query.filter.html index 583744d0e67..632f89b8248 100644 --- a/public/app/plugins/datasource/stackdriver/partials/query.filter.html +++ b/public/app/plugins/datasource/stackdriver/partials/query.filter.html @@ -3,7 +3,7 @@ Service ; - service: string; - metricType: string; metricDescriptors: any[]; metrics: any[]; metricGroups: any[]; @@ -111,37 +109,12 @@ export class StackdriverFilterCtrl { } getServicesList() { - const services = this.metricDescriptors.map(m => { - return { - value: m.service, - label: _.startCase(m.serviceShortName), - }; - }); + const services = this.metricDescriptors.map(m => ({ + value: m.service, + label: _.startCase(m.serviceShortName), + })); - if (services.find(m => m.value === this.target.service)) { - this.service = this.target.service; - } - - return services.length > 0 ? _.uniqBy(services, 'value') : []; - } - - getMetricGroupsOld() { - return this.metrics.reduce((acc, curr) => { - const group = acc.find(group => group.service === curr.service); - if (group) { - group.options = [...group.options, { value: curr.value, label: curr.label }]; - } else { - acc = [ - ...acc, - { - label: _.startCase(curr.serviceShortName), - service: curr.service, - options: [{ value: curr.value, label: curr.label }], - }, - ]; - } - return acc; - }, []); + return services.length > 0 ? _.uniqBy(services, s => s.value) : []; } getMetricGroups() { @@ -149,7 +122,7 @@ export class StackdriverFilterCtrl { this.getTemplateVariablesGroup(), { label: 'Metrics', - options: this.metrics, + options: this.getMetricsList(), }, ]; } @@ -160,24 +133,18 @@ export class StackdriverFilterCtrl { options: this.templateSrv.variables.map(v => ({ label: `$${v.name}`, value: `$${v.name}`, - // description: `$${v.definition}`, })), }; } getMetricsList() { - const metrics = this.metricDescriptors.map(m => { - return { - service: m.service, - value: m.type, - serviceShortName: m.serviceShortName, - text: m.displayName, - label: m.displayName, - description: m.description, - }; - }); + const metricsByService = this.metricDescriptors.filter(m => m.service === this.target.service).map(m => ({ + service: m.service, + value: m.type, + label: m.displayName, + description: m.description, + })); - const metricsByService = metrics.filter(m => m.service === this.target.service); if ( metricsByService.length > 0 && !metricsByService.some(m => m.value === this.templateSrv.replace(this.target.metricType)) @@ -212,12 +179,12 @@ export class StackdriverFilterCtrl { } handleServiceChange(service) { - this.target.service = this.service = service; + this.target.service = service; this.metrics = this.getMetricsList(); this.metricGroups = this.getMetricGroups(); this.setMetricType(); this.getLabels(); - if (!this.metrics.find(m => m.value === this.target.metricType)) { + if (!this.metrics.some(m => m.value === this.target.metricType)) { this.target.metricType = ''; } else { this.$scope.refresh(); @@ -231,7 +198,6 @@ export class StackdriverFilterCtrl { } setMetricType() { - // this.target.metricType = this.metricType; const { valueType, metricKind, unit } = this.metricDescriptors.find( m => m.type === this.templateSrv.replace(this.target.metricType) ); From d632be849a713e4dd771c16091632bbfef7c0de3 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 12 Dec 2018 13:58:25 +0100 Subject: [PATCH 37/68] use same color for label as in explore dropdown --- public/sass/components/_group-heading.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/sass/components/_group-heading.scss b/public/sass/components/_group-heading.scss index 9656277d123..8fc39c0dc2a 100644 --- a/public/sass/components/_group-heading.scss +++ b/public/sass/components/_group-heading.scss @@ -1,5 +1,6 @@ .picker-option-group { cursor: default; + color: $text-color-weak; + font-size: $font-size-sm; font-weight: $lead-font-weight; - color: $btn-primary-bg-hl; } From de9ace731d2bb6e952f7f947fd7b93fadf8cff80 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 12 Dec 2018 13:59:13 +0100 Subject: [PATCH 38/68] fix remove filter bug --- .../app/plugins/datasource/stackdriver/query_filter_ctrl.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts b/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts index d270290abb2..8ce2dfde554 100644 --- a/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts +++ b/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts @@ -252,8 +252,10 @@ export class StackdriverFilterCtrl { return []; } - this.removeSegment.value = removeText; - return [...elements, this.removeSegment]; + return [ + ...elements, + this.uiSegmentSrv.newSegment({ fake: true, value: removeText || this.defaultRemoveGroupByValue }), + ]; } async getGroupBys(segment) { From 1090e0ae2f09132d587958c1ed54f6044b75e88c Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 12 Dec 2018 14:08:47 +0100 Subject: [PATCH 39/68] remove on metric type change --- .../datasource/stackdriver/query_filter_ctrl.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts b/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts index 8ce2dfde554..e617dba5945 100644 --- a/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts +++ b/public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts @@ -60,7 +60,9 @@ export class StackdriverFilterCtrl { handleMetricTypeChange(value) { this.target.metricType = value; - this.onMetricTypeChange(); + this.setMetricType(); + this.$scope.refresh(); + this.getLabels(); } initSegments(hideGroupBys: boolean) { @@ -191,12 +193,6 @@ export class StackdriverFilterCtrl { } } - async onMetricTypeChange() { - this.setMetricType(); - this.$scope.refresh(); - this.getLabels(); - } - setMetricType() { const { valueType, metricKind, unit } = this.metricDescriptors.find( m => m.type === this.templateSrv.replace(this.target.metricType) From 07f0d90718027f12abfdbbcbd7067790777807f9 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 18 Dec 2018 11:25:13 +0100 Subject: [PATCH 40/68] use new generic picker --- .../components/OptionGroupPicker.tsx | 52 ------------------ .../stackdriver/components/OptionPicker.tsx | 50 ----------------- .../components/StackdriverPicker.tsx | 53 +++++++++++++++++++ .../partials/query.aggregation.html | 16 +++--- .../stackdriver/partials/query.filter.html | 6 +-- .../datasource/stackdriver/query_ctrl.ts | 13 +---- 6 files changed, 66 insertions(+), 124 deletions(-) delete mode 100644 public/app/plugins/datasource/stackdriver/components/OptionGroupPicker.tsx delete mode 100644 public/app/plugins/datasource/stackdriver/components/OptionPicker.tsx create mode 100644 public/app/plugins/datasource/stackdriver/components/StackdriverPicker.tsx diff --git a/public/app/plugins/datasource/stackdriver/components/OptionGroupPicker.tsx b/public/app/plugins/datasource/stackdriver/components/OptionGroupPicker.tsx deleted file mode 100644 index f7863dd2a16..00000000000 --- a/public/app/plugins/datasource/stackdriver/components/OptionGroupPicker.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import Select from 'react-select'; -import _ from 'lodash'; - -import GroupHeading from 'app/core/components/Picker/GroupHeading'; -import DescriptionOption from 'app/core/components/Picker/DescriptionOption'; -import IndicatorsContainer from 'app/core/components/Picker/IndicatorsContainer'; -import ResetStyles from 'app/core/components/Picker/ResetStyles'; -import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage'; - -export interface Props { - onChange: (value: string) => void; - groups: any[]; - searchable: boolean; - selected: string; - placeholder?: string; - className?: string; -} - -export class OptionGroupPicker extends React.Component { - constructor(props) { - super(props); - } - - render() { - const { onChange, groups, selected, placeholder, className, searchable } = this.props; - const options = _.flatten(groups.map(o => o.options)); - const selectedOption = options.find(option => option.value === selected); - - return ( - onChange(option.value)} - getOptionValue={i => i.value} - getOptionLabel={i => i.label} - value={selectedOption} - noOptionsMessage={() => 'No metrics found'} - /> - ); - } -} diff --git a/public/app/plugins/datasource/stackdriver/components/StackdriverPicker.tsx b/public/app/plugins/datasource/stackdriver/components/StackdriverPicker.tsx new file mode 100644 index 00000000000..b2c447feeff --- /dev/null +++ b/public/app/plugins/datasource/stackdriver/components/StackdriverPicker.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import _ from 'lodash'; +import Select from 'app/core/components/Select/Select'; + +export interface Props { + onChange: (value: string) => void; + options: any[]; + searchable: boolean; + selected: string; + placeholder?: string; + className?: string; + groups?: boolean; +} + +export class StackdriverPicker extends React.Component { + constructor(props) { + super(props); + } + + extractOptions(options) { + return options.length > 0 && options.every(o => o.options) ? _.flatten(options.map(o => o.options)) : options; + } + + onChange = item => { + const extractedOptions = this.extractOptions(this.props.options); + const option = extractedOptions.find(option => option.value === item.value); + this.props.onChange(option.value); + }; + + render() { + const { options, selected, placeholder, className, searchable } = this.props; + const extractedOptions = this.extractOptions(options); + const selectedOption = extractedOptions.find(option => option.value === selected); + + return ( +
@@ -15,7 +15,7 @@
Project - +