D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
usr
/
share
/
grafana
/
public
/
app
/
plugins
/
datasource
/
influxdb
/
Filename :
response_parser.test.ts
back
Copy
import { size } from 'lodash'; import { of } from 'rxjs'; import { AnnotationEvent, DataFrame, DataQueryRequest, dateTime, FieldType, MutableDataFrame } from '@grafana/data'; import { FetchResponse } from '@grafana/runtime'; import config from 'app/core/config'; import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__ import InfluxQueryModel from './influx_query_model'; import ResponseParser, { getSelectedParams } from './response_parser'; import { getMockDS, getMockDSInstanceSettings } from './specs/mocks'; import { InfluxQuery } from './types'; jest.mock('@grafana/runtime', () => ({ ...jest.requireActual('@grafana/runtime'), getBackendSrv: () => backendSrv, })); describe('influxdb response parser', () => { const parser = new ResponseParser(); describe('SHOW TAG response', () => { const query = 'SHOW TAG KEYS FROM "cpu"'; const response = { results: [ { series: [ { name: 'cpu', columns: ['tagKey'], values: [['datacenter'], ['hostname'], ['source']], }, ], }, ], }; const result = parser.parse(query, response); it('expects three results', () => { expect(size(result)).toBe(3); }); }); describe('SHOW TAG VALUES response', () => { const query = 'SHOW TAG VALUES FROM "cpu" WITH KEY = "hostname"'; describe('response from 0.10.0', () => { const response = { results: [ { series: [ { name: 'hostnameTagValues', columns: ['hostname'], values: [['server1'], ['server2'], ['server2']], }, ], }, ], }; const result = parser.parse(query, response); it('should get two responses', () => { expect(size(result)).toBe(2); expect(result[0].text).toBe('server1'); expect(result[1].text).toBe('server2'); }); }); describe('response from 0.12.0', () => { const response = { results: [ { series: [ { name: 'cpu', columns: ['key', 'value'], values: [ ['source', 'site'], ['source', 'api'], ], }, { name: 'logins', columns: ['key', 'value'], values: [ ['source', 'site'], ['source', 'webapi'], ], }, ], }, ], }; const result = parser.parse(query, response); it('should get two responses', () => { expect(size(result)).toBe(3); expect(result[0].text).toBe('site'); expect(result[1].text).toBe('api'); expect(result[2].text).toBe('webapi'); }); }); }); describe('SELECT response', () => { const query = 'SELECT "usage_iowait" FROM "cpu" LIMIT 10'; const response = { results: [ { series: [ { name: 'cpu', columns: ['time', 'usage_iowait'], values: [ [1488465190006040638, 0.0], [1488465190006040638, 15.0], [1488465190006040638, 20.2], ], }, ], }, ], }; const result = parser.parse(query, response); it('should return second column', () => { expect(size(result)).toBe(3); expect(result[0].text).toBe('0'); expect(result[1].text).toBe('15'); expect(result[2].text).toBe('20.2'); }); }); describe('SELECT response where ordering matters', () => { const query = 'SELECT "val" from "num"'; const response = { results: [ { series: [ { name: 'num', columns: ['time', 'val'], values: [ [1620041231000, 2], [1620041233000, 3], [1620041235000, 4], [1620041238000, 5], [1620041239000, 1], ], }, ], }, ], }; it('should keep the order returned by influxdb, even for numbers', () => { expect(parser.parse(query, response)).toStrictEqual([ { text: '2' }, { text: '3' }, { text: '4' }, { text: '5' }, { text: '1' }, ]); }); }); describe('SHOW FIELD response', () => { const query = 'SHOW FIELD KEYS FROM "cpu"'; describe('response from pre-1.0', () => { const response = { results: [ { series: [ { name: 'cpu', columns: ['fieldKey'], values: [['value']], }, ], }, ], }; const result = parser.parse(query, response); it('should get two responses', () => { expect(size(result)).toBe(1); }); }); describe('response from 1.0', () => { const response = { results: [ { series: [ { name: 'cpu', columns: ['fieldKey', 'fieldType'], values: [['time', 'float']], }, ], }, ], }; const result = parser.parse(query, response); it('should return first column', () => { expect(size(result)).toBe(1); expect(result[0].text).toBe('time'); }); }); }); describe('Should name the selected params correctly', () => { it('when there are no duplicates', () => { const query = new InfluxQueryModel({ refId: 'A', select: [[{ type: 'field', params: ['usage_iowait'] }], [{ type: 'field', params: ['usage_idle'] }]], }); const selectedParams = getSelectedParams(query.target); expect(selectedParams.length).toBe(2); expect(selectedParams[0]).toBe('usage_iowait'); expect(selectedParams[1]).toBe('usage_idle'); }); it('when there are duplicates', () => { const query = new InfluxQueryModel({ refId: 'A', select: [ [{ type: 'field', params: ['usage_iowait'] }], [{ type: 'field', params: ['usage_iowait'] }], [{ type: 'field', params: ['usage_iowait'] }], [{ type: 'field', params: ['usage_idle'] }], ], }); const selectedParams = getSelectedParams(query.target); expect(selectedParams.length).toBe(4); expect(selectedParams[0]).toBe('usage_iowait'); expect(selectedParams[1]).toBe('usage_iowait_1'); expect(selectedParams[2]).toBe('usage_iowait_2'); expect(selectedParams[3]).toBe('usage_idle'); }); }); describe('Should get the table', () => { const dataFrame = new MutableDataFrame({ fields: [ { name: 'time', type: FieldType.time, values: [1640257340000] }, { name: 'value', type: FieldType.number, values: [3234232323] }, ], meta: { executedQueryString: 'SELECT everything!', }, }); const query = new InfluxQueryModel({ refId: 'A', select: [[{ type: 'field', params: ['usage_iowait'] }], [{ type: 'field', params: ['usage_idle'] }]], }); const table = parser.getTable([dataFrame], query.target, { preferredVisualisationType: 'table', }); it('columns correctly', () => { expect(table.columns.length).toBe(3); expect(table.columns[0].text).toBe('Time'); expect(table.columns[1].text).toBe('usage_iowait'); expect(table.columns[2].text).toBe('usage_idle'); }); it('rows correctly', () => { expect(table.rows.length).toBe(1); expect(table.rows[0].length).toBe(2); expect(table.rows[0][0]).toBe(1640257340000); expect(table.rows[0][1]).toBe(3234232323); }); it('preferredVisualisationType correctly', () => { expect(table.meta?.preferredVisualisationType).toBe('table'); }); it('executedQueryString correctly', () => { expect(table.meta?.executedQueryString).toBe('SELECT everything!'); }); }); describe('table with aliases', () => { it('should parse the table with alias', () => { const table = parser.getTable(mockDataFramesWithAlias, mockQuery, { preferredVisualisationType: 'table' }); expect(table.columns.length).toBe(4); expect(table.columns[0].text).toBe('Time'); expect(table.columns[1].text).toBe('geohash'); expect(table.columns[2].text).toBe('ALIAS1'); expect(table.columns[3].text).toBe('ALIAS2'); }); it('should parse the table when there is no alias and two field selects', () => { const table = parser.getTable(mockDataframesWithTwoFieldSelect, mockQueryWithTwoFieldSelect, { preferredVisualisationType: 'table', }); expect(table.columns.length).toBe(3); expect(table.columns[0].text).toBe('Time'); expect(table.columns[1].text).toBe('mean'); expect(table.columns[2].text).toBe('mean_1'); }); }); describe('When issuing annotationQuery', () => { const ctx = { ds: getMockDS(getMockDSInstanceSettings()), }; const fetchMock = jest.spyOn(backendSrv, 'fetch'); const annotation: InfluxQuery = { refId: 'A', fromAnnotations: true, name: 'Anno', query: 'select * from logs where time >= now() - 15m and time <= now()', textColumn: 'textColumn', tagsColumn: 'host,path', }; const queryOptions: DataQueryRequest = { app: 'explore', interval: '', intervalMs: 0, requestId: '', scopedVars: {}, startTime: 0, timezone: '', targets: [annotation], range: { from: dateTime().subtract(1, 'h'), to: dateTime(), raw: { from: '1h', to: 'now' }, }, }; let response: AnnotationEvent[]; beforeEach(async () => { fetchMock.mockImplementation(() => { return of(annotationMockResponse); }); config.featureToggles.influxdbBackendMigration = true; response = await ctx.ds.annotationEvents(queryOptions, annotation); }); it('should return annotation list', () => { expect(response.length).toBe(2); expect(response[0].time).toBe(1645208701000); expect(response[0].title).toBe('Station softwareupdated[447]: Adding client 1'); expect(response[0].text).toBe('text 1'); expect(response[0].tags?.[0]).toBe('cbfa07e0e3bb 1'); expect(response[0].tags?.[1]).toBe('/var/log/host/install.log 1'); expect(response[1].time).toBe(1645208702000); expect(response[1].title).toBe('Station softwareupdated[447]: Adding client 2'); expect(response[1].text).toBe('text 2'); expect(response[1].tags?.[0]).toBe('cbfa07e0e3bb 2'); expect(response[1].tags?.[1]).toBe('/var/log/host/install.log 2'); }); }); }); const mockQuery: InfluxQuery = { datasource: { type: 'influxdb', uid: '12345', }, groupBy: [ { params: ['$__interval'], type: 'time', }, { type: 'tag', params: ['geohash::tag'], }, { params: ['null'], type: 'fill', }, ], measurement: 'cpu', orderByTime: 'ASC', policy: 'bar', refId: 'A', resultFormat: 'table', select: [ [ { type: 'field', params: ['value'], }, { type: 'mean', params: [], }, { type: 'alias', params: ['ALIAS1'], }, ], [ { type: 'field', params: ['value'], }, { type: 'mean', params: [], }, { type: 'alias', params: ['ALIAS2'], }, ], ], tags: [], }; const mockDataFramesWithAlias: DataFrame[] = [ { name: 'cpu.ALIAS1 { geohash: tz6h548nc111 }', refId: 'A', meta: { executedQueryString: 'SELECT mean("value") AS "ALIAS1", mean("value") AS "ALIAS2" FROM "bar"."cpu" WHERE time >= 1686582333244ms and time <= 1686583233244ms GROUP BY time(500ms), "geohash"::tag fill(null) ORDER BY time ASC', }, fields: [ { name: 'Time', type: FieldType.time, config: {}, values: [1686582333000, 1686582333500, 1686582334000], }, { name: 'Value', type: FieldType.number, labels: { geohash: 'tz6h548nc111', }, config: { displayNameFromDS: 'cpu.ALIAS1 { geohash: tz6h548nc111 }', }, values: [null, 111.98024577663908, null], }, ], length: 1801, }, { name: 'cpu.ALIAS2 { geohash: tz6h548nc111 }', refId: 'A', meta: { executedQueryString: 'SELECT mean("value") AS "ALIAS1", mean("value") AS "ALIAS2" FROM "bar"."cpu" WHERE time >= 1686582333244ms and time <= 1686583233244ms GROUP BY time(500ms), "geohash"::tag fill(null) ORDER BY time ASC', }, fields: [ { name: 'Time', type: FieldType.time, config: {}, values: [1686582333000, 1686582333500, 1686582334000], }, { name: 'Value', type: FieldType.number, labels: { geohash: 'tz6h548nc111', }, config: { displayNameFromDS: 'cpu.ALIAS2 { geohash: tz6h548nc111 }', }, values: [null, 111.98024577663908, null], }, ], length: 1801, }, { name: 'cpu.ALIAS1 { geohash: wj7c61wnv111 }', refId: 'A', meta: { executedQueryString: 'SELECT mean("value") AS "ALIAS1", mean("value") AS "ALIAS2" FROM "bar"."cpu" WHERE time >= 1686582333244ms and time <= 1686583233244ms GROUP BY time(500ms), "geohash"::tag fill(null) ORDER BY time ASC', }, fields: [ { name: 'Time', type: FieldType.time, config: {}, values: [1686582333000, 1686582333500, 1686582334000], }, { name: 'Value', type: FieldType.number, labels: { geohash: 'wj7c61wnv111', }, config: { displayNameFromDS: 'cpu.ALIAS1 { geohash: wj7c61wnv111 }', }, values: [null, 112.97136059147347, null], }, ], length: 1801, }, { name: 'cpu.ALIAS2 { geohash: wj7c61wnv111 }', refId: 'A', meta: { executedQueryString: 'SELECT mean("value") AS "ALIAS1", mean("value") AS "ALIAS2" FROM "bar"."cpu" WHERE time >= 1686582333244ms and time <= 1686583233244ms GROUP BY time(500ms), "geohash"::tag fill(null) ORDER BY time ASC', }, fields: [ { name: 'Time', type: FieldType.time, config: {}, values: [1686582333000, 1686582333500, 1686582334000], }, { name: 'Value', type: FieldType.number, labels: { geohash: 'wj7c61wnv111', }, config: { displayNameFromDS: 'cpu.ALIAS2 { geohash: wj7c61wnv111 }', }, values: [null, 112.97136059147347, null], }, ], length: 1801, }, { name: 'cpu.ALIAS1 { geohash: wr50zpuhj111 }', refId: 'A', meta: { executedQueryString: 'SELECT mean("value") AS "ALIAS1", mean("value") AS "ALIAS2" FROM "bar"."cpu" WHERE time >= 1686582333244ms and time <= 1686583233244ms GROUP BY time(500ms), "geohash"::tag fill(null) ORDER BY time ASC', }, fields: [ { name: 'Time', type: FieldType.time, config: {}, values: [1686582333000, 1686582333500, 1686582334000], }, { name: 'Value', type: FieldType.number, labels: { geohash: 'wr50zpuhj111', }, config: { displayNameFromDS: 'cpu.ALIAS1 { geohash: wr50zpuhj111 }', }, values: [null, 112.27638560052755, null], }, ], length: 1801, }, { name: 'cpu.ALIAS2 { geohash: wr50zpuhj111 }', refId: 'A', meta: { executedQueryString: 'SELECT mean("value") AS "ALIAS1", mean("value") AS "ALIAS2" FROM "bar"."cpu" WHERE time >= 1686582333244ms and time <= 1686583233244ms GROUP BY time(500ms), "geohash"::tag fill(null) ORDER BY time ASC', }, fields: [ { name: 'Time', type: FieldType.time, config: {}, values: [1686582333000, 1686582333500, 1686582334000], }, { name: 'Value', type: FieldType.number, labels: { geohash: 'wr50zpuhj111', }, config: { displayNameFromDS: 'cpu.ALIAS2 { geohash: wr50zpuhj111 }', }, values: [null, 112.27638560052755, null], }, ], length: 1801, }, ]; const mockDataframesWithTwoFieldSelect: DataFrame[] = [ { name: 'cpu.mean', refId: 'A', meta: { typeVersion: [0, 0], executedQueryString: 'SELECT mean("value"), mean("value") FROM "bar"."cpu" WHERE time >= 1686585763070ms and time <= 1686585793070ms GROUP BY time(10ms) fill(null) ORDER BY time ASC', }, fields: [ { name: 'Time', type: FieldType.time, config: {}, values: [1686585763070, 1686585763080, 1686585763090], }, { name: 'Value', type: FieldType.number, config: { displayNameFromDS: 'cpu.mean', }, values: [null, 87.42703187930438, null], }, ], length: 3, }, { name: 'cpu.mean_1', refId: 'A', meta: { typeVersion: [0, 0], executedQueryString: 'SELECT mean("value"), mean("value") FROM "bar"."cpu" WHERE time >= 1686585763070ms and time <= 1686585793070ms GROUP BY time(10ms) fill(null) ORDER BY time ASC', }, fields: [ { name: 'Time', type: FieldType.time, config: {}, values: [1686585763070, 1686585763080, 1686585763090], }, { name: 'Value', type: FieldType.number, config: { displayNameFromDS: 'cpu.mean_1', }, values: [87.3, 87.4, 87.5], }, ], length: 3, }, ]; const mockQueryWithTwoFieldSelect: InfluxQuery = { datasource: { type: 'influxdb', uid: '1234', }, groupBy: [ { params: ['$__interval'], type: 'time', }, { params: ['null'], type: 'fill', }, ], measurement: 'cpu', orderByTime: 'ASC', policy: 'bar', refId: 'A', resultFormat: 'table', select: [ [ { type: 'field', params: ['value'], }, { type: 'mean', params: [], }, ], [ { type: 'field', params: ['value'], }, { type: 'mean', params: [], }, ], ], tags: [], }; const annotationMockResponse: FetchResponse = { config: { url: '' }, headers: new Headers(), ok: false, redirected: false, status: 0, statusText: '', type: 'basic', url: '', data: { results: { metricFindQuery: { frames: [ { schema: { name: 'logs.host', fields: [ { name: 'time', type: 'time', }, { name: 'value', type: 'string', }, ], }, data: { values: [ [1645208701000, 1645208702000], ['cbfa07e0e3bb 1', 'cbfa07e0e3bb 2'], ], }, }, { schema: { name: 'logs.message', fields: [ { name: 'time', type: 'time', }, { name: 'value', type: 'string', }, ], }, data: { values: [ [1645208701000, 1645208702000], ['Station softwareupdated[447]: Adding client 1', 'Station softwareupdated[447]: Adding client 2'], ], }, }, { schema: { name: 'logs.path', fields: [ { name: 'time', type: 'time', }, { name: 'value', type: 'string', }, ], }, data: { values: [ [1645208701000, 1645208702000], ['/var/log/host/install.log 1', '/var/log/host/install.log 2'], ], }, }, { schema: { name: 'textColumn', fields: [ { name: 'time', type: 'time', }, { name: 'value', type: 'string', }, ], }, data: { values: [ [1645208701000, 1645208702000], ['text 1', 'text 2'], ], }, }, ], }, }, }, };