Skip to content

Commit

Permalink
MySQL, Postgres, MSSQL: Fix validating query with template variables …
Browse files Browse the repository at this point in the history
…in alert (#19237)

Adds support for validating query in alert for mysql, 
postgres and mssql.

Fixes #13155
  • Loading branch information
marefr committed Sep 24, 2019
1 parent f203e82 commit 96046a7
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 10 deletions.
5 changes: 5 additions & 0 deletions public/app/plugins/datasource/mssql/datasource.ts
Expand Up @@ -165,4 +165,9 @@ export class MssqlDatasource {
}
});
}

targetContainsTemplate(target: any) {
const rawSql = target.rawSql.replace('$__', '');
return this.templateSrv.variableExists(rawSql);
}
}
56 changes: 52 additions & 4 deletions public/app/plugins/datasource/mssql/specs/datasource.test.ts
@@ -1,23 +1,24 @@
import { MssqlDatasource } from '../datasource';
import { TemplateSrvStub, TimeSrvStub } from 'test/specs/helpers';
import { TimeSrvStub } from 'test/specs/helpers';
import { CustomVariable } from 'app/features/templating/custom_variable';
// @ts-ignore
import q from 'q';
import { dateTime } from '@grafana/data';
import { TemplateSrv } from 'app/features/templating/template_srv';

describe('MSSQLDatasource', () => {
const templateSrv: TemplateSrv = new TemplateSrv();

const ctx: any = {
backendSrv: {},
// @ts-ignore
templateSrv: new TemplateSrvStub(),
timeSrv: new TimeSrvStub(),
};

beforeEach(() => {
ctx.$q = q;
ctx.instanceSettings = { name: 'mssql' };

ctx.ds = new MssqlDatasource(ctx.instanceSettings, ctx.backendSrv, ctx.$q, ctx.templateSrv, ctx.timeSrv);
ctx.ds = new MssqlDatasource(ctx.instanceSettings, ctx.backendSrv, ctx.$q, templateSrv, ctx.timeSrv);
});

describe('When performing annotationQuery', () => {
Expand Down Expand Up @@ -278,4 +279,51 @@ describe('MSSQLDatasource', () => {
});
});
});

describe('targetContainsTemplate', () => {
it('given query that contains template variable it should return true', () => {
const rawSql = `SELECT
$__timeGroup(createdAt,'$summarize') as time,
avg(value) as value,
hostname as metric
FROM
grafana_metric
WHERE
$__timeFilter(createdAt) AND
measurement = 'logins.count' AND
hostname IN($host)
GROUP BY $__timeGroup(createdAt,'$summarize'), hostname
ORDER BY 1`;
const query = {
rawSql,
};
templateSrv.init([
{ type: 'query', name: 'summarize', current: { value: '1m' } },
{ type: 'query', name: 'host', current: { value: 'a' } },
]);
expect(ctx.ds.targetContainsTemplate(query)).toBeTruthy();
});

it('given query that only contains global template variable it should return false', () => {
const rawSql = `SELECT
$__timeGroup(createdAt,'$__interval') as time,
avg(value) as value,
hostname as metric
FROM
grafana_metric
WHERE
$__timeFilter(createdAt) AND
measurement = 'logins.count'
GROUP BY $__timeGroup(createdAt,'$summarize'), hostname
ORDER BY 1`;
const query = {
rawSql,
};
templateSrv.init([
{ type: 'query', name: 'summarize', current: { value: '1m' } },
{ type: 'query', name: 'host', current: { value: 'a' } },
]);
expect(ctx.ds.targetContainsTemplate(query)).toBeFalsy();
});
});
});
15 changes: 15 additions & 0 deletions public/app/plugins/datasource/mysql/datasource.ts
Expand Up @@ -175,4 +175,19 @@ export class MysqlDatasource {
}
});
}

targetContainsTemplate(target: any) {
let rawSql = '';

if (target.rawQuery) {
rawSql = target.rawSql;
} else {
const query = new MysqlQuery(target);
rawSql = query.buildQuery();
}

rawSql = rawSql.replace('$__', '');

return this.templateSrv.variableExists(rawSql);
}
}
54 changes: 51 additions & 3 deletions public/app/plugins/datasource/mysql/specs/datasource.test.ts
Expand Up @@ -2,13 +2,12 @@ import { MysqlDatasource } from '../datasource';
import { CustomVariable } from 'app/features/templating/custom_variable';
import { toUtc, dateTime } from '@grafana/data';
import { BackendSrv } from 'app/core/services/backend_srv';
import { TemplateSrv } from 'app/features/templating/template_srv';

describe('MySQLDatasource', () => {
const instanceSettings = { name: 'mysql' };
const backendSrv = {};
const templateSrv: any = {
replace: jest.fn(text => text),
};
const templateSrv: TemplateSrv = new TemplateSrv();

const raw = {
from: toUtc('2018-04-25 10:00'),
Expand Down Expand Up @@ -240,4 +239,53 @@ describe('MySQLDatasource', () => {
});
});
});

describe('targetContainsTemplate', () => {
it('given query that contains template variable it should return true', () => {
const rawSql = `SELECT
$__timeGroup(createdAt,'$summarize') as time_sec,
avg(value) as value,
hostname as metric
FROM
grafana_metric
WHERE
$__timeFilter(createdAt) AND
measurement = 'logins.count' AND
hostname IN($host)
GROUP BY 1, 3
ORDER BY 1`;
const query = {
rawSql,
rawQuery: true,
};
templateSrv.init([
{ type: 'query', name: 'summarize', current: { value: '1m' } },
{ type: 'query', name: 'host', current: { value: 'a' } },
]);
expect(ctx.ds.targetContainsTemplate(query)).toBeTruthy();
});

it('given query that only contains global template variable it should return false', () => {
const rawSql = `SELECT
$__timeGroup(createdAt,'$__interval') as time_sec,
avg(value) as value,
hostname as metric
FROM
grafana_metric
WHERE
$__timeFilter(createdAt) AND
measurement = 'logins.count'
GROUP BY 1, 3
ORDER BY 1`;
const query = {
rawSql,
rawQuery: true,
};
templateSrv.init([
{ type: 'query', name: 'summarize', current: { value: '1m' } },
{ type: 'query', name: 'host', current: { value: 'a' } },
]);
expect(ctx.ds.targetContainsTemplate(query)).toBeFalsy();
});
});
});
15 changes: 15 additions & 0 deletions public/app/plugins/datasource/postgres/datasource.ts
Expand Up @@ -160,4 +160,19 @@ export class PostgresDatasource {
}
});
}

targetContainsTemplate(target: any) {
let rawSql = '';

if (target.rawQuery) {
rawSql = target.rawSql;
} else {
const query = new PostgresQuery(target);
rawSql = query.buildQuery();
}

rawSql = rawSql.replace('$__', '');

return this.templateSrv.variableExists(rawSql);
}
}
54 changes: 51 additions & 3 deletions public/app/plugins/datasource/postgres/specs/datasource.test.ts
Expand Up @@ -3,14 +3,13 @@ import { CustomVariable } from 'app/features/templating/custom_variable';
import { toUtc, dateTime } from '@grafana/data';
import { BackendSrv } from 'app/core/services/backend_srv';
import { IQService } from 'angular';
import { TemplateSrv } from 'app/features/templating/template_srv';

describe('PostgreSQLDatasource', () => {
const instanceSettings = { name: 'postgresql' };

const backendSrv = {};
const templateSrv: any = {
replace: jest.fn(text => text),
};
const templateSrv: TemplateSrv = new TemplateSrv();
const raw = {
from: toUtc('2018-04-25 10:00'),
to: toUtc('2018-04-25 11:00'),
Expand Down Expand Up @@ -249,4 +248,53 @@ describe('PostgreSQLDatasource', () => {
});
});
});

describe('targetContainsTemplate', () => {
it('given query that contains template variable it should return true', () => {
const rawSql = `SELECT
$__timeGroup("createdAt",'$summarize'),
avg(value) as "value",
hostname as "metric"
FROM
grafana_metric
WHERE
$__timeFilter("createdAt") AND
measurement = 'logins.count' AND
hostname IN($host)
GROUP BY time, metric
ORDER BY time`;
const query = {
rawSql,
rawQuery: true,
};
templateSrv.init([
{ type: 'query', name: 'summarize', current: { value: '1m' } },
{ type: 'query', name: 'host', current: { value: 'a' } },
]);
expect(ctx.ds.targetContainsTemplate(query)).toBeTruthy();
});

it('given query that only contains global template variable it should return false', () => {
const rawSql = `SELECT
$__timeGroup("createdAt",'$__interval'),
avg(value) as "value",
hostname as "metric"
FROM
grafana_metric
WHERE
$__timeFilter("createdAt") AND
measurement = 'logins.count'
GROUP BY time, metric
ORDER BY time`;
const query = {
rawSql,
rawQuery: true,
};
templateSrv.init([
{ type: 'query', name: 'summarize', current: { value: '1m' } },
{ type: 'query', name: 'host', current: { value: 'a' } },
]);
expect(ctx.ds.targetContainsTemplate(query)).toBeFalsy();
});
});
});

0 comments on commit 96046a7

Please sign in to comment.