diff --git a/README.md b/README.md index f7d34cf..e4f5ca2 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,12 @@ Whether the alerts gathered should be inhibited. ![Parameters](https://raw.githubusercontent.com/camptocamp/grafana-prometheus-alertmanager-datasource/master/img/table.png) +# Variable Query Editor + +Additionally, to the query editor options, the `Field` must be filled whose values are used for the variable. + +![Parameters](https://raw.githubusercontent.com/camptocamp/grafana-prometheus-alertmanager-datasource/master/img/variablequeryeditor.png) + # Panels ## Stat diff --git a/img/variablequeryeditor.png b/img/variablequeryeditor.png new file mode 100644 index 0000000..f7a3a3b Binary files /dev/null and b/img/variablequeryeditor.png differ diff --git a/src/DataSource.ts b/src/DataSource.ts index 4d46400..d10b2d7 100644 --- a/src/DataSource.ts +++ b/src/DataSource.ts @@ -3,10 +3,13 @@ import { DataSourceApi, DataSourceInstanceSettings, FieldType, + MetricFindValue, MutableDataFrame, + ScopedVars, } from '@grafana/data'; import { getBackendSrv, getTemplateSrv } from '@grafana/runtime'; import { GenericOptions, CustomQuery, QueryRequest, defaultQuery } from './types'; +import { lastValueFrom } from 'rxjs'; export class AlertmanagerDataSource extends DataSourceApi { url: string; @@ -25,8 +28,8 @@ export class AlertmanagerDataSource extends DataSourceApi { - const promises = options.targets.map((query) => { + async doQuery(queries: CustomQuery[], scopedVars?: ScopedVars): Promise { + const promises = queries.map((query) => { query = { ...defaultQuery, ...query }; if (query.hide) { return Promise.resolve(new MutableDataFrame()); @@ -43,31 +46,70 @@ export class AlertmanagerDataSource extends DataSourceApi 0) { - query.filters = getTemplateSrv().replace(query.filters, options.scopedVars, this.interpolateQueryExpr); + query.filters = getTemplateSrv().replace(query.filters, scopedVars, this.interpolateQueryExpr); query.filters.split(',').forEach((value) => { params.push(`filter=${encodeURIComponent(value)}`); }); } - const request = this.doRequest({ + return this.doRequest({ url: `${this.url}/api/v2/alerts?${params.join('&')}`, method: 'GET', - }).then((request) => request.toPromise()); - - return request.then((data: any) => this.retrieveData(query, data)); + }) + .then((data) => lastValueFrom(data)) + .then((data) => { + return this.retrieveData(query, data); + }) + .catch(() => { + return new MutableDataFrame(); + }); }); return Promise.all(promises).then((data) => { + return data; + }); + } + + async query(options: QueryRequest): Promise { + return this.doQuery(options.targets, options.scopedVars).then((data) => { return { data }; }); } + async metricFindQuery(query: CustomQuery, options?: any): Promise { + if (typeof query.field === undefined) { + return []; + } + const response = (await this.doQuery([query], options.scopedVars))[0]; + + let fieldIndex = -1; + response.fields.forEach((field, index) => { + if (field.name === query.field) { + fieldIndex = index; + } + }); + if (fieldIndex === -1) { + return []; + } + + const values = response.fields[fieldIndex].values.toArray().map((val) => { + return val.toString(); + }); + const unique = [...new Set(values)]; + return unique.map((val) => { + return { text: val }; + }); + } + async testDatasource() { return this.doRequest({ url: this.url, method: 'GET', - }).then((response) => - response.toPromise().then((data) => { + }) + .then((response) => { + return lastValueFrom(response); + }) + .then((data) => { if (data !== undefined) { if (data.ok) { return { status: 'success', message: 'Datasource is working', title: 'Success' }; @@ -84,8 +126,7 @@ export class AlertmanagerDataSource extends DataSourceApi { onActiveChange = () => { const { onChange, query, onRunQuery } = this.props; - query.active = !query.active; - onChange({ ...query }); + let newQuery = { ...query }; + newQuery.active = !query.active; + onChange(newQuery); onRunQuery(); }; onSilencedChange = () => { const { onChange, query, onRunQuery } = this.props; - query.silenced = !query.silenced; - onChange({ ...query }); + let newQuery = { ...query }; + newQuery.silenced = !query.silenced; + onChange(newQuery); onRunQuery(); }; onInhibitedChange = () => { const { onChange, query, onRunQuery } = this.props; - query.inhibited = !query.inhibited; - onChange({ ...query }); + let newQuery = { ...query }; + newQuery.inhibited = !query.inhibited; + onChange(newQuery); onRunQuery(); }; - render() { - const { receiver, filters, active, silenced, inhibited } = { ...defaultQuery, ...this.props.query }; + onFieldChange = (event: ChangeEvent) => { + const { onChange, query, onRunQuery } = this.props; + onChange({ ...query, field: event.target.value }); + onRunQuery(); + }; + + baseRender(showField: boolean) { + let { receiver, filters, active, silenced, inhibited, field } = { ...defaultQuery, ...this.props.query }; return ( <> @@ -70,6 +79,18 @@ export class QueryEditor extends PureComponent { label="Filters (comma separated key=value)" /> + {showField && ( +
+ +
+ )}
@@ -83,4 +104,8 @@ export class QueryEditor extends PureComponent { ); } + + render() { + return this.baseRender(false); + } } diff --git a/src/VariableQueryEditor.tsx b/src/VariableQueryEditor.tsx new file mode 100644 index 0000000..b7982f2 --- /dev/null +++ b/src/VariableQueryEditor.tsx @@ -0,0 +1,7 @@ +import { QueryEditor } from './QueryEditor'; + +export class CustomVariableQueryEditor extends QueryEditor { + render() { + return super.baseRender(true); + } +} diff --git a/src/module.ts b/src/module.ts index 08d9634..5d300da 100644 --- a/src/module.ts +++ b/src/module.ts @@ -3,6 +3,7 @@ import { ConfigEditor } from './ConfigEditor'; import { AlertmanagerDataSource } from './DataSource'; import { QueryEditor } from './QueryEditor'; import { CustomQuery, GenericOptions } from './types'; +import { CustomVariableQueryEditor } from './VariableQueryEditor'; class GenericAnnotationsQueryCtrl { static templateUrl = 'partials/annotations.editor.html'; @@ -11,4 +12,5 @@ class GenericAnnotationsQueryCtrl { export const plugin = new DataSourcePlugin(AlertmanagerDataSource) .setAnnotationQueryCtrl(GenericAnnotationsQueryCtrl) .setConfigEditor(ConfigEditor) - .setQueryEditor(QueryEditor); + .setQueryEditor(QueryEditor) + .setVariableQueryEditor(CustomVariableQueryEditor); diff --git a/src/types.ts b/src/types.ts index 0d1e6ae..d5bf8b3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,7 +1,5 @@ import { DataQuery, DataQueryRequest, DataSourceJsonData } from '@grafana/data'; -export interface DataSourceOptions extends DataSourceJsonData {} - export interface QueryRequest extends DataQueryRequest { adhocFilters?: any[]; } @@ -13,6 +11,7 @@ export interface CustomQuery extends DataQuery { active: boolean; silenced: boolean; inhibited: boolean; + field: string; } export const defaultQuery: Partial = {