diff --git a/src/DataSource.ts b/src/DataSource.ts index 3369d7b..0818c3c 100644 --- a/src/DataSource.ts +++ b/src/DataSource.ts @@ -6,7 +6,14 @@ import { MutableDataFrame, } from '@grafana/data'; import { getBackendSrv, getTemplateSrv } from '@grafana/runtime'; -import { GenericOptions, CustomQuery, QueryRequest } from './types'; +import { + AlertStateLabel, + GenericOptions, + CustomQuery, + QueryRequest, + FullDisplayMode, + SummaryDisplayMode, +} from './types'; export class AlertmanagerDataSource extends DataSourceApi { url: string; @@ -86,7 +93,7 @@ export class AlertmanagerDataSource extends DataSourceApi 0) { @@ -104,29 +111,108 @@ export class AlertmanagerDataSource extends DataSourceApi = []; + if (query.active) { + fields.push({ + name: 'Active', + type: FieldType.number, + }); + } + if (query.silenced) { + fields.push({ + name: 'Silenced', + type: FieldType.number, + }); + } + if (query.inhibited) { + fields.push({ + name: 'Inhibited', + type: FieldType.number, + }); + } + const frame = new MutableDataFrame({ + refId: query.refId, fields: fields, }); return frame; } - parseAlertAttributes(alert: any, fields: any[]): string[] { + parseAlertAttributes(alert: any, fields: any[], displayState: boolean): string[] { const row: string[] = [alert.startsAt]; fields.slice(1).forEach((element: any) => { - row.push(alert.annotations[element.name] || alert.labels[element.name] || ''); + if (displayState && element.name === AlertStateLabel) { + row.push(alert.status.state); + } else { + row.push(alert.annotations[element.name] || alert.labels[element.name] || ''); + } }); return row; } - retrieveData(query: any, data: any): Promise { - const frame = this.buildDataFrame(query.refId, data.data); + parseAlertCounts(query: any, data: any): string[] { + let active = 0; + let silenced = 0; + let inhibited = 0; data.data.forEach((alert: any) => { - const row: string[] = this.parseAlertAttributes(alert, frame.fields); - frame.appendRow(row); + let state: string = alert.status.state; + switch (state) { + case 'active': { + active++; + break; + } + case 'suppressed': { + silenced++; + break; + } + case 'inhibited': { + inhibited++; + break; + } + } }); - return Promise.resolve(frame); + const row: string[] = []; + if (query.active) { + row.push(active.toString()); + } + if (query.silenced) { + row.push(silenced.toString()); + } + if (query.inhibited) { + row.push(inhibited.toString()); + } + return row; + } + + retrieveData(query: any, data: any): Promise { + if (query.displayMode === SummaryDisplayMode) { + const frame = this.buildSummaryDataFrame(query); + frame.appendRow(this.parseAlertCounts(query, data)); + return Promise.resolve(frame); + } else if (query.displayMode === FullDisplayMode) { + const frame = this.buildDataFrame(query, data.data); + data.data.forEach((alert: any) => { + const row: string[] = this.parseAlertAttributes(alert, frame.fields, query.displayState); + frame.appendRow(row); + }); + return Promise.resolve(frame); + } else { + return Promise.resolve(new MutableDataFrame()); + } } } diff --git a/src/QueryEditor.tsx b/src/QueryEditor.tsx index 55bdcdc..7c81287 100644 --- a/src/QueryEditor.tsx +++ b/src/QueryEditor.tsx @@ -1,10 +1,11 @@ import { QueryEditorProps } from '@grafana/data'; -import { LegacyForms } from '@grafana/ui'; +import { InlineFormLabel, LegacyForms } from '@grafana/ui'; +import { RadioButtonGroup } from '@grafana/ui'; import React, { ChangeEvent, PureComponent } from 'react'; import { AlertmanagerDataSource } from './DataSource'; -import { GenericOptions, CustomQuery } from './types'; +import { GenericOptions, CustomQuery, FullDisplayMode, SummaryDisplayMode } from './types'; import './css/json-editor.css'; @@ -12,6 +13,11 @@ type Props = QueryEditorProps { onReceiverChange = (event: ChangeEvent) => { const { onChange, query, onRunQuery } = this.props; @@ -46,30 +52,55 @@ export class QueryEditor extends PureComponent { onRunQuery(); }; + onDisplayModeChange = (option: string | undefined) => { + const { onChange, query, onRunQuery } = this.props; + if (option === undefined) { + query.displayMode = FullDisplayMode; + } else { + query.displayMode = option; + } + onChange({ ...query }); + onRunQuery(); + }; + + onDisplayStateChange = () => { + const { onChange, query, onRunQuery } = this.props; + query.displayState = !query.displayState; + onChange({ ...query }); + onRunQuery(); + }; + render() { - const { receiver, filters, active, silenced, inhibited } = this.props.query; + const { receiver, filters, active, silenced, inhibited, displayMode, displayState } = this.props.query; return ( <> -
-
- -
-
- +
+
+
+ +
+
+ +
+
+
+ + Selected States +
@@ -80,6 +111,26 @@ export class QueryEditor extends PureComponent {
+
+ + Display Mode + + + options={DisplayModeOptions} + value={displayMode} + onChange={this.onDisplayModeChange} + /> + +
); } diff --git a/src/css/json-editor.css b/src/css/json-editor.css index 727cade..c833631 100644 --- a/src/css/json-editor.css +++ b/src/css/json-editor.css @@ -4,4 +4,8 @@ .grafana-json-datasource-editor div { width: 100%; +} + +row { + display: block; } \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index 24da034..f541d55 100644 --- a/src/types.ts +++ b/src/types.ts @@ -6,6 +6,11 @@ export interface QueryRequest extends DataQueryRequest { adhocFilters?: any[]; } +export const FullDisplayMode = 'full'; +export const SummaryDisplayMode = 'summary'; +export const DefaultDisplayMode = FullDisplayMode; +export const AlertStateLabel = 'Alert State'; + export interface CustomQuery extends DataQuery { target?: string; receiver: string; @@ -13,8 +18,10 @@ export interface CustomQuery extends DataQuery { active: boolean; silenced: boolean; inhibited: boolean; + displayMode: string; + displayState: boolean; } -export const defaultQuery: Partial = {}; +export const defaultQuery: Partial = { displayMode: DefaultDisplayMode }; export interface GenericOptions extends DataSourceJsonData {}