Skip to content

Commit

Permalink
Merge pull request #79 from grafana/fix-for-variable-query-to-backend
Browse files Browse the repository at this point in the history
Fix for variable query to backend
  • Loading branch information
srclosson authored Mar 11, 2020
2 parents 2bbad74 + c498c0e commit 4f5c0ac
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 160 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

All notable changes to this project will be documented in this file.

## [2.0.5]
- Bugfix for issue #61. This is a temp fix, as a proper fix requires refactoring some of the backend.

## [2.0.4]

- Bugfix for issue #73
Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ test-in-docker: build-container
build:
go build -o ./dist/${DSNAME}_${OS}_${ARCH}${EXT} -a -tags netgo -ldflags '-w' ./pkg

build-dev:
go build -o ./dist/${DSNAME}_${OS}_${ARCH}${EXT} -a -tags netgo -ldflags ./pkg

build-linux:
GOOS=linux go build -o ./dist/${DSNAME}_linux_${ARCH}${EXT} -a -tags netgo -ldflags '-w' ./pkg

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "grafana-azure-data-explorer-datasource",
"version": "2.0.4",
"version": "2.0.5",
"description": "Grafana data source for Azure Data Explorer",
"scripts": {
"build": "grafana-toolkit plugin:build && find src | grep '\\.d\\.ts$' | xargs rm -fv",
Expand Down
152 changes: 0 additions & 152 deletions src/datasource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,158 +112,6 @@ describe('KustoDBDatasource', () => {
});
});

describe('When performing metricFindQuery', () => {
const tableResponseWithOneColumn = {
Tables: [
{
TableName: 'Table_0',
Columns: [
{
ColumnName: 'Category',
ColumnType: 'string',
},
],
Rows: [['Administrative'], ['Policy']],
},
],
};

const databasesResponse = {
Tables: [
{
TableName: 'Table_0',
Columns: [
{ ColumnName: 'DatabaseName', DataType: 'String' },
{ ColumnName: 'PersistentStorage', DataType: 'String' },
{ ColumnName: 'Version', DataType: 'String' },
{ ColumnName: 'IsCurrent', DataType: 'Boolean' },
{ ColumnName: 'DatabaseAccessMode', DataType: 'String' },
{ ColumnName: 'PrettyName', DataType: 'String' },
{
ColumnName: 'CurrentUserIsUnrestrictedViewer',
DataType: 'Boolean',
},
{ ColumnName: 'DatabaseId', DataType: 'Guid' },
],
Rows: [
[
'Grafana',
'https://4bukustoragekus86a3c.blob.core.windows.net/grafanamd201806201624130602',
'v5.2',
false,
'ReadWrite',
null,
false,
'a955a3ed-0668-4d00-a2e5-9c4e610ef057',
],
],
},
],
};

let queryResults;

beforeEach(async () => {
ctx.backendSrv.datasourceRequest = options => {
if (options.url.indexOf('rest/mgmt') > -1) {
return ctx.$q.when({ data: databasesResponse, status: 200 });
} else {
return ctx.$q.when({ data: tableResponseWithOneColumn, status: 200 });
}
};

queryResults = await ctx.ds.metricFindQuery('Activity | distinct Category');
});

it('should return a list of categories in the correct format', () => {
expect(queryResults.length).toBe(2);
expect(queryResults[0].text).toBe('Administrative');
expect(queryResults[0].value).toBe('Administrative');
expect(queryResults[1].text).toBe('Policy');
expect(queryResults[1].value).toBe('Policy');
});
});

describe('When performing metricFindQuery (two columns)', () => {
const tableResponseWithTwoColumns = {
Tables: [
{
TableName: 'Table_1',
Columns: [
{
ColumnName: 'CatagoryId',
ColumnType: 'int',
},
{
ColumnName: 'CategoryName',
ColumnType: 'string',
},
],
Rows: [[12], ['Titanic'], [13], ['Titanic']],
},
],
};

const databasesResponse = {
Tables: [
{
TableName: 'Table_0',
Columns: [
{ ColumnName: 'DatabaseName', DataType: 'String' },
{ ColumnName: 'PersistentStorage', DataType: 'String' },
{ ColumnName: 'Version', DataType: 'String' },
{ ColumnName: 'IsCurrent', DataType: 'Boolean' },
{ ColumnName: 'DatabaseAccessMode', DataType: 'String' },
{ ColumnName: 'PrettyName', DataType: 'String' },
{
ColumnName: 'CurrentUserIsUnrestrictedViewer',
DataType: 'Boolean',
},
{ ColumnName: 'DatabaseId', DataType: 'Guid' },
],
Rows: [
[
'Grafana',
'https://4bukustoragekus86a3c.blob.core.windows.net/grafanamd201806201624130602',
'v5.2',
false,
'ReadWrite',
null,
false,
'a955a3ed-0668-4d00-a2e5-9c4e610ef057',
],
],
},
],
};

let queryResults;

beforeEach(async () => {
ctx.backendSrv.datasourceRequest = options => {
if (options.url.indexOf('rest/mgmt') > -1) {
return ctx.$q.when({ data: databasesResponse, status: 200 });
} else {
return ctx.$q.when({ data: tableResponseWithTwoColumns, status: 200 });
}
};

queryResults = await ctx.ds.metricFindQuery('Activity | project __value = CategoryId, __text = CategoryName');
});

it('should return a list of categories in the correct format', () => {
expect(queryResults.length).toBe(4);
expect(queryResults[0].text).toBe(12);
expect(queryResults[0].value).toBe(12);
expect(queryResults[1].text).toBe('Titanic');
expect(queryResults[1].value).toBe('Titanic');
expect(queryResults[2].text).toBe(13);
expect(queryResults[2].value).toBe(13);
expect(queryResults[3].text).toBe('Titanic');
expect(queryResults[3].value).toBe('Titanic');
});
});

describe('when performing annotations query', () => {
const tableResponse = {
Tables: [
Expand Down
19 changes: 14 additions & 5 deletions src/datasource.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import _ from 'lodash';
import { MetricFindValue } from '@grafana/data';
import { ResponseParser, DatabaseItem } from './response_parser';
import QueryBuilder from './query_builder';
import Cache from './cache';
Expand Down Expand Up @@ -143,17 +144,25 @@ export class KustoDBDatasource {
});
}

metricFindQuery(query: string, optionalOptions: any) {
metricFindQuery(query: string, optionalOptions: any): Promise<MetricFindValue[]> {
return this.getDefaultOrFirstDatabase()
.then(database => this.buildQuery(query, optionalOptions, database))
.then(queries =>
this.backendSrv.datasourceRequest({
url: '/api/tsdb/query',
method: 'POST',
queries,
data: {
from: '5m',
to: 'now',
queries,
},
})
)
.then(response => new ResponseParser().parseToVariables(response))
.then(response => {
const responseParser = new ResponseParser();
const processedResposne = responseParser.processQueryResult(response);
return responseParser.processVariableQueryResult(processedResposne);
})
.catch(err => {
console.log('There was an error', err);
throw err;
Expand Down Expand Up @@ -254,10 +263,10 @@ export class KustoDBDatasource {
}

private buildQuery(query: string, options: any, database: string) {
if (typeof options === 'undefined') {
if (!options) {
options = {};
}
if (typeof options.scopedVars === 'undefined') {
if (!options.hasOwnProperty('scopedVars')) {
options['scopedVars'] = {};
}
const queryBuilder = new QueryBuilder(this.templateSrv.replace(query, options.scopedVars, this.interpolateVariable), options);
Expand Down
104 changes: 104 additions & 0 deletions src/response_parser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { ResponseParser } from './response_parser';

describe('ResponseParser', () => {
it('Parse query responses for metrics', () => {
const mockedResponse = {
data: [
{
columns: [
{
text: 'print_0',
},
{
text: 'print_1',
},
{
text: 'print_2',
},
],
rows: [['hello', 'doctor', 'name', 'continue', 'yesterday', 'tomorrow']],
type: 'table',
refId: 'A',
meta: {
KustoError: '',
RawQuery: "print 'hello', 'doctor', 'name', 'continue', 'yesterday', 'tomorrow'",
TimeNotASC: false,
},
},
],
};

const mockedParsedResponse = [
{
text: 'hello',
},
{
text: 'doctor',
},
{
text: 'name',
},
{
text: 'continue',
},
{
text: 'yesterday',
},
{
text: 'tomorrow',
},
];

const rp = new ResponseParser();
const parsedResponse = rp.processVariableQueryResult(mockedResponse);
expect(parsedResponse).toStrictEqual(mockedParsedResponse);
});

it('Parse query responses for metrics', () => {
const mockedResponse = {
data: [
{
columns: [
{
text: 'cheese',
},
{
text: 'cities',
},
],
rows: [
['gouda', 'Paris'],
['chedder', 'New York'],
['mozza', 'Rome'],
['old', 'Warsaw'],
['squeaky', 'Montreal'],
],
type: 'table',
refId: 'A',
meta: {
KustoError: '',
RawQuery: "print 'hello', 'doctor', 'name', 'continue', 'yesterday', 'tomorrow'",
TimeNotASC: false,
},
},
],
};

const mockedParsedResponse = [
{ text: 'gouda' },
{ text: 'Paris' },
{ text: 'chedder' },
{ text: 'New York' },
{ text: 'mozza' },
{ text: 'Rome' },
{ text: 'old' },
{ text: 'Warsaw' },
{ text: 'squeaky' },
{ text: 'Montreal' },
];

const rp = new ResponseParser();
const parsedResponse = rp.processVariableQueryResult(mockedResponse);
expect(parsedResponse).toStrictEqual(mockedParsedResponse);
});
});
30 changes: 28 additions & 2 deletions src/response_parser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import _ from 'lodash';
import { dateTime } from '@grafana/data';
import { dateTime, MetricFindValue } from '@grafana/data';

export interface DataTarget {
target: string;
Expand Down Expand Up @@ -327,7 +327,11 @@ export class ResponseParser {
return dateTime(dt).valueOf();
}

// TODO(Temp Comment): processQueryResult is for results using the backend plugin
/**
* Translates the response from the backend plugin into a different format
* @todo figure out what that format is? Is it a documented standard?
* @param res The response from the backend plugin
*/
processQueryResult(res) {
const data: any[] = [];

Expand Down Expand Up @@ -382,4 +386,26 @@ export class ResponseParser {
valueCount: Object.keys(valueMap).length,
};
}

/**
* Processes the response further for MetricFindValues
* @todo Get rid of all the any's! We need to be strongly typing everything.
* @param res The return value from processQueryResult
* @returns An array of MetricFindValues
*/
processVariableQueryResult(res: any): MetricFindValue[] {
const ret: MetricFindValue[] = [];
res.data.forEach(dataEntry => {
dataEntry.rows.forEach((r: string[] | string) => {
if (Array.isArray(r)) {
const rArray: string[] = r;
rArray.forEach((item: string) => ret.push({ text: item }));
} else {
ret.push({ text: (r as unknown) as string });
}
});
});

return ret;
}
}

0 comments on commit 4f5c0ac

Please sign in to comment.