diff --git a/README.md b/README.md index 95c4b64..4be3dfa 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ To learn more about building dashboards, see the [Grafana docs][9]. The Sensu Go Data Source supports query strings with the structure: - QUERY API (entity|events|namespaces) [IN NAMESPACE (namespace)] SELECT (field-key) [WHERE (field-key)(=|!=|=~|!~|<|>)(field-value) [AND (field-key)(=|!=|=~|!~|<|>)(field-value)]] + QUERY API (entity|events|namespaces) [IN NAMESPACE (namespace)] SELECT (field-key) [WHERE (field-key)(=|!=|=~|!~|<|>)(field-value) [AND (field-key)(=|!=|=~|!~|<|>)(field-value)]] [LIMIT (limit)] > Note: Query keywords are case sensitive. @@ -91,10 +91,10 @@ For example, the following query returns hostnames containing the string `webser QUERY API entity SELECT system.hostname WHERE system.hostname=~/webserver/ ``` -The following query returns entity hostnames with active, non-OK events within the `ops` namespace: +The following query returns 100 entity hostnames with active, non-OK events within the `ops` namespace: ``` -QUERY API events IN NAMESPACE ops SELECT entity.system.hostname WHERE check.status>0 +QUERY API events IN NAMESPACE ops SELECT entity.system.hostname WHERE check.status>0 LIMIT 100 ``` The following query returns all namespaces with names starting with `x`: diff --git a/src/constants.ts b/src/constants.ts index 4033de0..8321df2 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -2,6 +2,16 @@ import AggregationType from './model/AggregationType'; import ApiEndpoint from './model/ApiEndpoint'; import TextValue from './model/TextValue'; +/** + * The default limit. + */ +export const DEFAULT_LIMIT: number = 100; + +/** + * The default limit for aggregation queries. + */ +export const DEFAULT_AGGREGATION_LIMIT: number = 0; + /** * Supported aggregation functions. */ diff --git a/src/datasource.ts b/src/datasource.ts index fe13c8e..9171054 100644 --- a/src/datasource.ts +++ b/src/datasource.ts @@ -2,7 +2,7 @@ import _ from 'lodash'; import sensu from './sensu/sensu'; import SensuQueryOptions from './sensu/query_options'; -import {API_ENDPOINTS} from './constants'; +import { API_ENDPOINTS, DEFAULT_LIMIT, DEFAULT_AGGREGATION_LIMIT } from './constants'; import FieldSelector from './FieldSelector'; import FilterUtils from './utils/datasource_filter_util'; import QueryUtils from './utils/query_util'; @@ -52,7 +52,7 @@ export default class SensuDatasource { * Returns the url of the API used by the given target. */ _getApiUrl = target => { - const apiEndpoint: any = _.find(API_ENDPOINTS, {value: target.apiEndpoints}); + const apiEndpoint: any = _.find(API_ENDPOINTS, { value: target.apiEndpoints }); if (apiEndpoint) { return apiEndpoint.url; } else { @@ -97,21 +97,31 @@ export default class SensuDatasource { ); if (queryTargets.length === 0) { - return Promise.resolve({data: []}); + return Promise.resolve({ data: [] }); } const queries = queryTargets.map(prepTarget => { const { apiUrl, filters, - target: {queryType, fieldSelectors, namespace, limit}, + target: { queryType, fieldSelectors, namespace, limit }, } = prepTarget; + // verify and set correct limit + let parsedLimit: number = _.defaultTo(parseInt(limit), -1); + if (parsedLimit < 0) { + if (queryType === 'aggregation') { + parsedLimit = DEFAULT_AGGREGATION_LIMIT; + } else { + parsedLimit = DEFAULT_LIMIT; + } + } + const queryOptions: SensuQueryOptions = { method: 'GET', url: apiUrl, namespace: namespace, - limit: limit, + limit: parsedLimit, }; return sensu @@ -131,19 +141,19 @@ export default class SensuDatasource { return Promise.all(queries).then((queryResults: any) => { if (options.resultAsPlainArray) { - // return only values - e.g. for template variables + // return only values - e.g. for template variables return _(queryResults) .map(result => this._transformToTable(result)) .map(result => result.rows) .flatten() .flatten() .map(value => { - return {text: value}; + return { text: value }; }) .value(); } else { const resultDataList: any[] = _.flatMap(queryResults, (queryResult, index) => { - const {target: {format}} = queryTargets[index]; + const { target: { format } } = queryTargets[index]; if (format === 'series') { // return time series format @@ -166,7 +176,7 @@ export default class SensuDatasource { */ _queryAggregation = (data: any[], prepTarget: PreparedTarget) => { const { - target: {aggregationAlias: alias, aggregationType: type, aggregationField: field}, + target: { aggregationAlias: alias, aggregationType: type, aggregationField: field }, } = prepTarget; const name = alias ? alias : type; @@ -433,7 +443,7 @@ export default class SensuDatasource { * Transforms the given query components into an options object which can be used by the `query(..)` function. */ _transformQueryComponentsToQueryOptions = (queryComponents: QueryComponents) => { - const {apiKey, selectedField, filters, namespace} = queryComponents; + const { apiKey, selectedField, filters, namespace, limit } = queryComponents; const filterObjects = _.map(filters, filter => { return [ @@ -455,6 +465,7 @@ export default class SensuDatasource { apiEndpoints: apiKey, queryType: 'field', namespace: namespace, + limit: limit, fieldSelectors: [ { fieldSegments: [ @@ -485,7 +496,7 @@ export default class SensuDatasource { }; }) .catch(err => { - return {status: 'error', message: err.message}; + return { status: 'error', message: err.message }; }); } } diff --git a/src/model/QueryComponents.ts b/src/model/QueryComponents.ts index d4ab6f5..3f42343 100644 --- a/src/model/QueryComponents.ts +++ b/src/model/QueryComponents.ts @@ -5,4 +5,5 @@ export default interface QueryComponents { readonly namespace: string; readonly selectedField: string; readonly filters: Filter[]; + readonly limit: number; }; diff --git a/src/partials/query.editor.html b/src/partials/query.editor.html index f41adea..1e529c2 100644 --- a/src/partials/query.editor.html +++ b/src/partials/query.editor.html @@ -28,7 +28,7 @@