Skip to content

Commit

Permalink
Introduced the possibility to change the limit for aggregation querie…
Browse files Browse the repository at this point in the history
…s. (#11)
  • Loading branch information
mariusoe authored Sep 5, 2019
1 parent ef90efb commit 897792a
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 46 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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`:
Expand Down
10 changes: 10 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
33 changes: 22 additions & 11 deletions src/datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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;

Expand Down Expand Up @@ -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 [
Expand All @@ -455,6 +465,7 @@ export default class SensuDatasource {
apiEndpoints: apiKey,
queryType: 'field',
namespace: namespace,
limit: limit,
fieldSelectors: [
{
fieldSegments: [
Expand Down Expand Up @@ -485,7 +496,7 @@ export default class SensuDatasource {
};
})
.catch(err => {
return {status: 'error', message: err.message};
return { status: 'error', message: err.message };
});
}
}
1 change: 1 addition & 0 deletions src/model/QueryComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export default interface QueryComponents {
readonly namespace: string;
readonly selectedField: string;
readonly filters: Filter[];
readonly limit: number;
};
31 changes: 19 additions & 12 deletions src/partials/query.editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

<!-- FILTER SEGMENTS -->
<div class="gf-form-inline" ng-repeat="segmentArray in ctrl.target.filterSegments">
<div class="gf-form">
<div class="gf-form-inline">
<label class="gf-form-label query-keyword width-8">{{$first ? 'FILTER' : ''}}</label>

<metric-segment ng-repeat="segment in segmentArray" segment="segment" get-options="ctrl.getFilterSegmentOptions(segment, $parent.$index, $index)"
Expand Down Expand Up @@ -76,17 +76,6 @@
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>

<div class="gf-form-inline">
<label class="gf-form-label query-keyword width-8">LIMIT</label>

<input type="text" placeholder="100" class="gf-form-input width-8" ng-model="ctrl.target.limit" ng-change="ctrl.refresh()"
ng-model-options="{updateOn: 'blur'}">

<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
</div>

<!-- AGGREGATION -->
Expand Down Expand Up @@ -120,6 +109,24 @@
</div>
</div>

<!-- LIMIT -->
<div class="gf-form-inline">
<label class="gf-form-label query-keyword width-8">
LIMIT
<info-popover mode="right-normal" ng-show="ctrl.target.queryType == 'aggregation'">
<b>Note:</b> the aggregation is done on the client-side (browser). This means that a lot of data will be transferred if the query returns a large result set.
The default limit is set to <code>0</code> in aggregation queries causing ignoring the limit feature in order to ensure correct aggregation values.
</info-popover>
</label>

<input type="text" placeholder="{{ctrl.target.queryType == 'field' ? 100 : 0}}" class="gf-form-input width-8" ng-model="ctrl.target.limit" ng-change="ctrl.refresh()"
ng-model-options="{updateOn: 'blur'}">

<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>

<!-- FORMAT -->
<div class="gf-form-inline">
<label class="gf-form-label query-keyword width-8">FORMAT AS</label>
Expand Down
2 changes: 1 addition & 1 deletion src/sensu/query_options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ export default interface QueryOptions {
method: string;
url: string;
namespace: string;
limit?: string;
limit: number;
forceAccessTokenRefresh?: boolean;
};
16 changes: 4 additions & 12 deletions src/sensu/sensu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default class Sensu {
* @param options the options specifying the query's request
*/
static query(datasource: any, options: QueryOptions) {
const {method, url, namespace, limit, forceAccessTokenRefresh} = options;
const { method, url, namespace, limit, forceAccessTokenRefresh } = options;
if (forceAccessTokenRefresh) {
delete datasource.instanceSettings.tokens;
}
Expand All @@ -38,21 +38,13 @@ export default class Sensu {
fullUrl = Sensu.apiBaseUrl + '/namespaces/' + namespace + url;
}

let queryLimitString: string;
if (limit) {
queryLimitString = limit;
} else {
queryLimitString = '100';
}

const queryLimit = _.defaultTo(parseInt(queryLimitString), 100);
if (queryLimit > 0) {
fullUrl += '?limit=' + queryLimit;
if (limit > 0) {
fullUrl += '?limit=' + limit;
}

return this._authenticate(datasource)
.then(() => this._request(datasource, method, fullUrl))
.catch(() => this.query(datasource, {...options, forceAccessTokenRefresh: true}));
.catch(() => this.query(datasource, { ...options, forceAccessTokenRefresh: true }));
}

/**
Expand Down
16 changes: 9 additions & 7 deletions src/utils/query_util.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import _ from 'lodash';
import { DEFAULT_LIMIT, DEFAULT_AGGREGATION_LIMIT } from '../constants';

import QueryComponents from '../model/QueryComponents';

/**
* The default limit.
*/
const DEFAULT_LIMIT: number = 100;

const QUERY_FULL_REG_EXP = /QUERY\s+API\s+(entity|events|namespaces)\s+(IN\s+NAMESPACE\s+(\S+)\s+)?SELECT\s+(\S+)(\s+WHERE\s+(\S+\s*(=~?|!=|>|<|!=)\s*\S+(\s+AND\s+\S+\s*(=~?|!=|>|<|!=)\s*\S+)*))?/;
const QUERY_FULL_REG_EXP = /QUERY\s+API\s+(entity|events|namespaces)\s+(IN\s+NAMESPACE\s+(\S+)\s+)?SELECT\s+(\S+)(\s+WHERE\s+(\S+\s*(=~?|!=|>|<|!=)\s*\S+(\s+AND\s+\S+\s*(=~?|!=|>|<|!=)\s*\S+)*))?(\s+LIMIT\s+(\d+))?/;
const QUERY_SINGLE_FILTER_REG_EXP = /(\S+)\s*(=~?|!=|>|<|!~)\s*(\S+)/g;

/**
Expand Down Expand Up @@ -100,7 +96,12 @@ const _whereClause = (target: any) => {
const _limit = (target: any) => {
let queryLimit = target.limit;
if (!target.limit) {
queryLimit = DEFAULT_LIMIT;
// Use a special default limit in aggregation queries
if (target.queryType === 'aggregation') {
queryLimit = DEFAULT_AGGREGATION_LIMIT;
} else {
queryLimit = DEFAULT_LIMIT;
}
}

queryLimit = _.defaultTo(parseInt(queryLimit), DEFAULT_LIMIT);
Expand Down Expand Up @@ -130,6 +131,7 @@ export const extractQueryComponents = (query: string) => {
namespace: namespace,
selectedField: matchResult[4],
filters: [],
limit: parseInt(matchResult[11])
};

if (matchResult[6] !== undefined) {
Expand Down

0 comments on commit 897792a

Please sign in to comment.