mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Postgres/MySQL/MSSQL: Fix so that numeric/non-string values are returned from query variables (#35411)
Fixes a problem with query variables where SQL query returning for example only numeric values didn't populate the query variables with values. Fixes #35391
This commit is contained in:
parent
dc7a641978
commit
bf7301e485
@ -1,5 +1,4 @@
|
|||||||
import { map } from 'lodash';
|
import { AnnotationEvent, DataFrame, MetricFindValue } from '@grafana/data';
|
||||||
import { AnnotationEvent, DataFrame, FieldType, MetricFindValue } from '@grafana/data';
|
|
||||||
import { BackendDataSourceResponse, toDataQueryResponse, FetchResponse } from '@grafana/runtime';
|
import { BackendDataSourceResponse, toDataQueryResponse, FetchResponse } from '@grafana/runtime';
|
||||||
|
|
||||||
export default class ResponseParser {
|
export default class ResponseParser {
|
||||||
@ -21,16 +20,13 @@ export default class ResponseParser {
|
|||||||
values.push({ text: '' + textField.values.get(i), value: '' + valueField.values.get(i) });
|
values.push({ text: '' + textField.values.get(i), value: '' + valueField.values.get(i) });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const textFields = frame.fields.filter((f) => f.type === FieldType.string);
|
values.push(
|
||||||
if (textFields) {
|
...frame.fields
|
||||||
values.push(
|
.flatMap((f) => f.values.toArray())
|
||||||
...textFields
|
.map((v) => ({
|
||||||
.flatMap((f) => f.values.toArray())
|
text: v,
|
||||||
.map((v) => ({
|
}))
|
||||||
text: '' + v,
|
);
|
||||||
}))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.from(new Set(values.map((v) => v.text))).map((text) => ({
|
return Array.from(new Set(values.map((v) => v.text))).map((text) => ({
|
||||||
@ -39,53 +35,6 @@ export default class ResponseParser {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
transformToKeyValueList(rows: any, textColIndex: number, valueColIndex: number): MetricFindValue[] {
|
|
||||||
const res = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < rows.length; i++) {
|
|
||||||
if (!this.containsKey(res, rows[i][textColIndex])) {
|
|
||||||
res.push({ text: rows[i][textColIndex], value: rows[i][valueColIndex] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
transformToSimpleList(rows: any): MetricFindValue[] {
|
|
||||||
const res = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < rows.length; i++) {
|
|
||||||
for (let j = 0; j < rows[i].length; j++) {
|
|
||||||
res.push(rows[i][j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const unique = Array.from(new Set(res));
|
|
||||||
|
|
||||||
return map(unique, (value) => {
|
|
||||||
return { text: value };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
findColIndex(columns: any[], colName: string) {
|
|
||||||
for (let i = 0; i < columns.length; i++) {
|
|
||||||
if (columns[i].text === colName) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
containsKey(res: any[], key: any) {
|
|
||||||
for (let i = 0; i < res.length; i++) {
|
|
||||||
if (res[i].text === key) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async transformAnnotationResponse(options: any, data: BackendDataSourceResponse): Promise<AnnotationEvent[]> {
|
async transformAnnotationResponse(options: any, data: BackendDataSourceResponse): Promise<AnnotationEvent[]> {
|
||||||
const frames = toDataQueryResponse({ data: data }).data as DataFrame[];
|
const frames = toDataQueryResponse({ data: data }).data as DataFrame[];
|
||||||
const frame = frames[0];
|
const frame = frames[0];
|
||||||
|
@ -84,7 +84,7 @@ describe('MSSQLDatasource', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('When performing metricFindQuery', () => {
|
describe('When performing metricFindQuery that returns multiple string fields', () => {
|
||||||
let results: MetricFindValue[];
|
let results: MetricFindValue[];
|
||||||
const query = 'select * from atable';
|
const query = 'select * from atable';
|
||||||
const response = {
|
const response = {
|
||||||
@ -156,6 +156,50 @@ describe('MSSQLDatasource', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('When performing metricFindQuery without key, value columns', () => {
|
||||||
|
let results: any;
|
||||||
|
const query = 'select id, values from atable';
|
||||||
|
const response = {
|
||||||
|
results: {
|
||||||
|
tempvar: {
|
||||||
|
refId: 'tempvar',
|
||||||
|
frames: [
|
||||||
|
dataFrameToJSON(
|
||||||
|
new MutableDataFrame({
|
||||||
|
fields: [
|
||||||
|
{ name: 'id', values: [1, 2, 3] },
|
||||||
|
{ name: 'values', values: ['test1', 'test2', 'test3'] },
|
||||||
|
],
|
||||||
|
meta: {
|
||||||
|
executedQueryString: 'select id, values from atable',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fetchMock.mockImplementation(() => of(createFetchResponse(response)));
|
||||||
|
|
||||||
|
return ctx.ds.metricFindQuery(query).then((data: any) => {
|
||||||
|
results = data;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return list of all field values as text', () => {
|
||||||
|
expect(results).toEqual([
|
||||||
|
{ text: 1 },
|
||||||
|
{ text: 2 },
|
||||||
|
{ text: 3 },
|
||||||
|
{ text: 'test1' },
|
||||||
|
{ text: 'test2' },
|
||||||
|
{ text: 'test3' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('When performing metricFindQuery with key, value columns and with duplicate keys', () => {
|
describe('When performing metricFindQuery with key, value columns and with duplicate keys', () => {
|
||||||
let results: any;
|
let results: any;
|
||||||
const query = 'select * from atable';
|
const query = 'select * from atable';
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { map } from 'lodash';
|
import { AnnotationEvent, DataFrame, MetricFindValue } from '@grafana/data';
|
||||||
import { AnnotationEvent, DataFrame, FieldType, MetricFindValue } from '@grafana/data';
|
|
||||||
import { BackendDataSourceResponse, FetchResponse, toDataQueryResponse } from '@grafana/runtime';
|
import { BackendDataSourceResponse, FetchResponse, toDataQueryResponse } from '@grafana/runtime';
|
||||||
|
|
||||||
export default class ResponseParser {
|
export default class ResponseParser {
|
||||||
@ -21,16 +20,13 @@ export default class ResponseParser {
|
|||||||
values.push({ text: '' + textField.values.get(i), value: '' + valueField.values.get(i) });
|
values.push({ text: '' + textField.values.get(i), value: '' + valueField.values.get(i) });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const textFields = frame.fields.filter((f) => f.type === FieldType.string);
|
values.push(
|
||||||
if (textFields) {
|
...frame.fields
|
||||||
values.push(
|
.flatMap((f) => f.values.toArray())
|
||||||
...textFields
|
.map((v) => ({
|
||||||
.flatMap((f) => f.values.toArray())
|
text: v,
|
||||||
.map((v) => ({
|
}))
|
||||||
text: '' + v,
|
);
|
||||||
}))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.from(new Set(values.map((v) => v.text))).map((text) => ({
|
return Array.from(new Set(values.map((v) => v.text))).map((text) => ({
|
||||||
@ -39,53 +35,6 @@ export default class ResponseParser {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
transformToKeyValueList(rows: any, textColIndex: number, valueColIndex: number): MetricFindValue[] {
|
|
||||||
const res = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < rows.length; i++) {
|
|
||||||
if (!this.containsKey(res, rows[i][textColIndex])) {
|
|
||||||
res.push({ text: rows[i][textColIndex], value: rows[i][valueColIndex] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
transformToSimpleList(rows: any): MetricFindValue[] {
|
|
||||||
const res = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < rows.length; i++) {
|
|
||||||
for (let j = 0; j < rows[i].length; j++) {
|
|
||||||
res.push(rows[i][j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const unique = Array.from(new Set(res));
|
|
||||||
|
|
||||||
return map(unique, (value) => {
|
|
||||||
return { text: value };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
findColIndex(columns: any[], colName: string) {
|
|
||||||
for (let i = 0; i < columns.length; i++) {
|
|
||||||
if (columns[i].text === colName) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
containsKey(res: any[], key: any) {
|
|
||||||
for (let i = 0; i < res.length; i++) {
|
|
||||||
if (res[i].text === key) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async transformAnnotationResponse(options: any, data: BackendDataSourceResponse): Promise<AnnotationEvent[]> {
|
async transformAnnotationResponse(options: any, data: BackendDataSourceResponse): Promise<AnnotationEvent[]> {
|
||||||
const frames = toDataQueryResponse({ data: data }).data as DataFrame[];
|
const frames = toDataQueryResponse({ data: data }).data as DataFrame[];
|
||||||
const frame = frames[0];
|
const frame = frames[0];
|
||||||
|
@ -121,7 +121,7 @@ describe('MySQLDatasource', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('When performing metricFindQuery', () => {
|
describe('When performing metricFindQuery that returns multiple string fields', () => {
|
||||||
const query = 'select * from atable';
|
const query = 'select * from atable';
|
||||||
const response = {
|
const response = {
|
||||||
results: {
|
results: {
|
||||||
@ -144,7 +144,7 @@ describe('MySQLDatasource', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should return list of all column values', async () => {
|
it('should return list of all string field values', async () => {
|
||||||
const { ds } = setupTextContext(response);
|
const { ds } = setupTextContext(response);
|
||||||
const results = await ds.metricFindQuery(query, {});
|
const results = await ds.metricFindQuery(query, {});
|
||||||
|
|
||||||
@ -257,6 +257,44 @@ describe('MySQLDatasource', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('When performing metricFindQuery without key, value columns', () => {
|
||||||
|
const query = 'select id, values from atable';
|
||||||
|
const response = {
|
||||||
|
results: {
|
||||||
|
tempvar: {
|
||||||
|
refId: 'tempvar',
|
||||||
|
frames: [
|
||||||
|
dataFrameToJSON(
|
||||||
|
new MutableDataFrame({
|
||||||
|
fields: [
|
||||||
|
{ name: 'id', values: [1, 2, 3] },
|
||||||
|
{ name: 'values', values: ['test1', 'test2', 'test3'] },
|
||||||
|
],
|
||||||
|
meta: {
|
||||||
|
executedQueryString: 'select id, values from atable',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should return list of all field values as text', async () => {
|
||||||
|
const { ds } = setupTextContext(response);
|
||||||
|
const results = await ds.metricFindQuery(query, {});
|
||||||
|
|
||||||
|
expect(results).toEqual([
|
||||||
|
{ text: 1 },
|
||||||
|
{ text: 2 },
|
||||||
|
{ text: 3 },
|
||||||
|
{ text: 'test1' },
|
||||||
|
{ text: 'test2' },
|
||||||
|
{ text: 'test3' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('When performing metricFindQuery with key, value columns and with duplicate keys', () => {
|
describe('When performing metricFindQuery with key, value columns and with duplicate keys', () => {
|
||||||
const query = 'select * from atable';
|
const query = 'select * from atable';
|
||||||
const response = {
|
const response = {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { AnnotationEvent, DataFrame, FieldType, MetricFindValue } from '@grafana/data';
|
import { AnnotationEvent, DataFrame, MetricFindValue } from '@grafana/data';
|
||||||
import { BackendDataSourceResponse, FetchResponse, toDataQueryResponse } from '@grafana/runtime';
|
import { BackendDataSourceResponse, FetchResponse, toDataQueryResponse } from '@grafana/runtime';
|
||||||
import { map } from 'lodash';
|
|
||||||
|
|
||||||
export default class ResponseParser {
|
export default class ResponseParser {
|
||||||
transformMetricFindResponse(raw: FetchResponse<BackendDataSourceResponse>): MetricFindValue[] {
|
transformMetricFindResponse(raw: FetchResponse<BackendDataSourceResponse>): MetricFindValue[] {
|
||||||
@ -21,16 +20,13 @@ export default class ResponseParser {
|
|||||||
values.push({ text: '' + textField.values.get(i), value: '' + valueField.values.get(i) });
|
values.push({ text: '' + textField.values.get(i), value: '' + valueField.values.get(i) });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const textFields = frame.fields.filter((f) => f.type === FieldType.string);
|
values.push(
|
||||||
if (textFields) {
|
...frame.fields
|
||||||
values.push(
|
.flatMap((f) => f.values.toArray())
|
||||||
...textFields
|
.map((v) => ({
|
||||||
.flatMap((f) => f.values.toArray())
|
text: v,
|
||||||
.map((v) => ({
|
}))
|
||||||
text: '' + v,
|
);
|
||||||
}))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.from(new Set(values.map((v) => v.text))).map((text) => ({
|
return Array.from(new Set(values.map((v) => v.text))).map((text) => ({
|
||||||
@ -39,56 +35,6 @@ export default class ResponseParser {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
transformToKeyValueList(rows: any, textColIndex: number, valueColIndex: number) {
|
|
||||||
const res = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < rows.length; i++) {
|
|
||||||
if (!this.containsKey(res, rows[i][textColIndex])) {
|
|
||||||
res.push({
|
|
||||||
text: rows[i][textColIndex],
|
|
||||||
value: rows[i][valueColIndex],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
transformToSimpleList(rows: any[][]) {
|
|
||||||
const res = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < rows.length; i++) {
|
|
||||||
for (let j = 0; j < rows[i].length; j++) {
|
|
||||||
res.push(rows[i][j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const unique = Array.from(new Set(res));
|
|
||||||
|
|
||||||
return map(unique, (value) => {
|
|
||||||
return { text: value };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
findColIndex(columns: any[], colName: string) {
|
|
||||||
for (let i = 0; i < columns.length; i++) {
|
|
||||||
if (columns[i].text === colName) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
containsKey(res: any, key: any) {
|
|
||||||
for (let i = 0; i < res.length; i++) {
|
|
||||||
if (res[i].text === key) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async transformAnnotationResponse(options: any, data: BackendDataSourceResponse): Promise<AnnotationEvent[]> {
|
async transformAnnotationResponse(options: any, data: BackendDataSourceResponse): Promise<AnnotationEvent[]> {
|
||||||
const frames = toDataQueryResponse({ data: data }).data as DataFrame[];
|
const frames = toDataQueryResponse({ data: data }).data as DataFrame[];
|
||||||
const frame = frames[0];
|
const frame = frames[0];
|
||||||
|
@ -338,8 +338,8 @@ describe('PostgreSQLDatasource', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('When performing metricFindQuery', () => {
|
describe('When performing metricFindQuery that returns multiple string fields', () => {
|
||||||
it('should return list of all column values', async () => {
|
it('should return list of all string field values', async () => {
|
||||||
const query = 'select * from atable';
|
const query = 'select * from atable';
|
||||||
const response = {
|
const response = {
|
||||||
results: {
|
results: {
|
||||||
@ -487,6 +487,43 @@ describe('PostgreSQLDatasource', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('When performing metricFindQuery without key, value columns', () => {
|
||||||
|
it('should return list of all field values as text', async () => {
|
||||||
|
const query = 'select id, values from atable';
|
||||||
|
const response = {
|
||||||
|
results: {
|
||||||
|
tempvar: {
|
||||||
|
refId: 'tempvar',
|
||||||
|
frames: [
|
||||||
|
dataFrameToJSON(
|
||||||
|
new MutableDataFrame({
|
||||||
|
fields: [
|
||||||
|
{ name: 'id', values: [1, 2, 3] },
|
||||||
|
{ name: 'values', values: ['test1', 'test2', 'test3'] },
|
||||||
|
],
|
||||||
|
meta: {
|
||||||
|
executedQueryString: 'select id, values from atable',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const { ds } = setupTestContext(response);
|
||||||
|
const results = await ds.metricFindQuery(query, {});
|
||||||
|
|
||||||
|
expect(results).toEqual([
|
||||||
|
{ text: 1 },
|
||||||
|
{ text: 2 },
|
||||||
|
{ text: 3 },
|
||||||
|
{ text: 'test1' },
|
||||||
|
{ text: 'test2' },
|
||||||
|
{ text: 'test3' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('When performing metricFindQuery with key, value columns and with duplicate keys', () => {
|
describe('When performing metricFindQuery with key, value columns and with duplicate keys', () => {
|
||||||
it('should return list of unique keys', async () => {
|
it('should return list of unique keys', async () => {
|
||||||
const query = 'select * from atable';
|
const query = 'select * from atable';
|
||||||
|
Loading…
Reference in New Issue
Block a user