response_parser.test.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. import { size } from 'lodash';
  2. import { of } from 'rxjs';
  3. import { TemplateSrvStub } from 'test/specs/helpers';
  4. import { FieldType, MutableDataFrame } from '@grafana/data';
  5. import { FetchResponse } from '@grafana/runtime';
  6. import config from 'app/core/config';
  7. import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__
  8. import InfluxDatasource from '../datasource';
  9. import InfluxQueryModel from '../influx_query_model';
  10. import ResponseParser, { getSelectedParams } from '../response_parser';
  11. //@ts-ignore
  12. const templateSrv = new TemplateSrvStub();
  13. jest.mock('@grafana/runtime', () => ({
  14. ...(jest.requireActual('@grafana/runtime') as unknown as object),
  15. getBackendSrv: () => backendSrv,
  16. }));
  17. describe('influxdb response parser', () => {
  18. const parser = new ResponseParser();
  19. describe('SHOW TAG response', () => {
  20. const query = 'SHOW TAG KEYS FROM "cpu"';
  21. const response = {
  22. results: [
  23. {
  24. series: [
  25. {
  26. name: 'cpu',
  27. columns: ['tagKey'],
  28. values: [['datacenter'], ['hostname'], ['source']],
  29. },
  30. ],
  31. },
  32. ],
  33. };
  34. const result = parser.parse(query, response);
  35. it('expects three results', () => {
  36. expect(size(result)).toBe(3);
  37. });
  38. });
  39. describe('SHOW TAG VALUES response', () => {
  40. const query = 'SHOW TAG VALUES FROM "cpu" WITH KEY = "hostname"';
  41. describe('response from 0.10.0', () => {
  42. const response = {
  43. results: [
  44. {
  45. series: [
  46. {
  47. name: 'hostnameTagValues',
  48. columns: ['hostname'],
  49. values: [['server1'], ['server2'], ['server2']],
  50. },
  51. ],
  52. },
  53. ],
  54. };
  55. const result = parser.parse(query, response);
  56. it('should get two responses', () => {
  57. expect(size(result)).toBe(2);
  58. expect(result[0].text).toBe('server1');
  59. expect(result[1].text).toBe('server2');
  60. });
  61. });
  62. describe('response from 0.12.0', () => {
  63. const response = {
  64. results: [
  65. {
  66. series: [
  67. {
  68. name: 'cpu',
  69. columns: ['key', 'value'],
  70. values: [
  71. ['source', 'site'],
  72. ['source', 'api'],
  73. ],
  74. },
  75. {
  76. name: 'logins',
  77. columns: ['key', 'value'],
  78. values: [
  79. ['source', 'site'],
  80. ['source', 'webapi'],
  81. ],
  82. },
  83. ],
  84. },
  85. ],
  86. };
  87. const result = parser.parse(query, response);
  88. it('should get two responses', () => {
  89. expect(size(result)).toBe(3);
  90. expect(result[0].text).toBe('site');
  91. expect(result[1].text).toBe('api');
  92. expect(result[2].text).toBe('webapi');
  93. });
  94. });
  95. });
  96. describe('SELECT response', () => {
  97. const query = 'SELECT "usage_iowait" FROM "cpu" LIMIT 10';
  98. const response = {
  99. results: [
  100. {
  101. series: [
  102. {
  103. name: 'cpu',
  104. columns: ['time', 'usage_iowait'],
  105. values: [
  106. [1488465190006040638, 0.0],
  107. [1488465190006040638, 15.0],
  108. [1488465190006040638, 20.2],
  109. ],
  110. },
  111. ],
  112. },
  113. ],
  114. };
  115. const result = parser.parse(query, response);
  116. it('should return second column', () => {
  117. expect(size(result)).toBe(3);
  118. expect(result[0].text).toBe('0');
  119. expect(result[1].text).toBe('15');
  120. expect(result[2].text).toBe('20.2');
  121. });
  122. });
  123. describe('SELECT response where ordering matters', () => {
  124. const query = 'SELECT "val" from "num"';
  125. const response = {
  126. results: [
  127. {
  128. series: [
  129. {
  130. name: 'num',
  131. columns: ['time', 'val'],
  132. values: [
  133. [1620041231000, 2],
  134. [1620041233000, 3],
  135. [1620041235000, 4],
  136. [1620041238000, 5],
  137. [1620041239000, 1],
  138. ],
  139. },
  140. ],
  141. },
  142. ],
  143. };
  144. it('should keep the order returned by influxdb, even for numbers', () => {
  145. expect(parser.parse(query, response)).toStrictEqual([
  146. { text: '2' },
  147. { text: '3' },
  148. { text: '4' },
  149. { text: '5' },
  150. { text: '1' },
  151. ]);
  152. });
  153. });
  154. describe('SHOW FIELD response', () => {
  155. const query = 'SHOW FIELD KEYS FROM "cpu"';
  156. describe('response from pre-1.0', () => {
  157. const response = {
  158. results: [
  159. {
  160. series: [
  161. {
  162. name: 'cpu',
  163. columns: ['fieldKey'],
  164. values: [['value']],
  165. },
  166. ],
  167. },
  168. ],
  169. };
  170. const result = parser.parse(query, response);
  171. it('should get two responses', () => {
  172. expect(size(result)).toBe(1);
  173. });
  174. });
  175. describe('response from 1.0', () => {
  176. const response = {
  177. results: [
  178. {
  179. series: [
  180. {
  181. name: 'cpu',
  182. columns: ['fieldKey', 'fieldType'],
  183. values: [['time', 'float']],
  184. },
  185. ],
  186. },
  187. ],
  188. };
  189. const result = parser.parse(query, response);
  190. it('should return first column', () => {
  191. expect(size(result)).toBe(1);
  192. expect(result[0].text).toBe('time');
  193. });
  194. });
  195. });
  196. describe('Should name the selected params correctly', () => {
  197. it('when there are no duplicates', () => {
  198. const query = new InfluxQueryModel({
  199. refId: 'A',
  200. select: [[{ type: 'field', params: ['usage_iowait'] }], [{ type: 'field', params: ['usage_idle'] }]],
  201. });
  202. const selectedParams = getSelectedParams(query.target);
  203. expect(selectedParams.length).toBe(2);
  204. expect(selectedParams[0]).toBe('usage_iowait');
  205. expect(selectedParams[1]).toBe('usage_idle');
  206. });
  207. it('when there are duplicates', () => {
  208. const query = new InfluxQueryModel({
  209. refId: 'A',
  210. select: [
  211. [{ type: 'field', params: ['usage_iowait'] }],
  212. [{ type: 'field', params: ['usage_iowait'] }],
  213. [{ type: 'field', params: ['usage_iowait'] }],
  214. [{ type: 'field', params: ['usage_idle'] }],
  215. ],
  216. });
  217. const selectedParams = getSelectedParams(query.target);
  218. expect(selectedParams.length).toBe(4);
  219. expect(selectedParams[0]).toBe('usage_iowait');
  220. expect(selectedParams[1]).toBe('usage_iowait_1');
  221. expect(selectedParams[2]).toBe('usage_iowait_2');
  222. expect(selectedParams[3]).toBe('usage_idle');
  223. });
  224. });
  225. describe('Should get the table', () => {
  226. const dataFrame = new MutableDataFrame({
  227. fields: [
  228. { name: 'time', type: FieldType.time, values: [1640257340000] },
  229. { name: 'value', type: FieldType.number, values: [3234232323] },
  230. ],
  231. meta: {
  232. executedQueryString: 'SELECT everything!',
  233. },
  234. });
  235. const query = new InfluxQueryModel({
  236. refId: 'A',
  237. select: [[{ type: 'field', params: ['usage_iowait'] }], [{ type: 'field', params: ['usage_idle'] }]],
  238. });
  239. const table = parser.getTable([dataFrame], query.target, {
  240. preferredVisualisationType: 'table',
  241. });
  242. it('columns correctly', () => {
  243. expect(table.columns.length).toBe(3);
  244. expect(table.columns[0].text).toBe('Time');
  245. expect(table.columns[1].text).toBe('usage_iowait');
  246. expect(table.columns[2].text).toBe('usage_idle');
  247. });
  248. it('rows correctly', () => {
  249. expect(table.rows.length).toBe(1);
  250. expect(table.rows[0].length).toBe(2);
  251. expect(table.rows[0][0]).toBe(1640257340000);
  252. expect(table.rows[0][1]).toBe(3234232323);
  253. });
  254. it('preferredVisualisationType correctly', () => {
  255. expect(table.meta?.preferredVisualisationType).toBe('table');
  256. });
  257. it('executedQueryString correctly', () => {
  258. expect(table.meta?.executedQueryString).toBe('SELECT everything!');
  259. });
  260. });
  261. describe('When issuing annotationQuery', () => {
  262. const ctx: any = {
  263. instanceSettings: { url: 'url', name: 'influxDb' },
  264. };
  265. const fetchMock = jest.spyOn(backendSrv, 'fetch');
  266. const queryOptions: any = {
  267. annotation: {
  268. name: 'Anno',
  269. query: 'select * from logs where time >= now() - 15m and time <= now()',
  270. textColumn: 'textColumn',
  271. tagsColumn: 'host,path',
  272. },
  273. range: {
  274. from: '2018-01-01T00:00:00Z',
  275. to: '2018-01-02T00:00:00Z',
  276. },
  277. };
  278. let response: any;
  279. beforeEach(async () => {
  280. fetchMock.mockImplementation(() => {
  281. return of({
  282. data: {
  283. results: {
  284. metricFindQuery: {
  285. frames: [
  286. {
  287. schema: {
  288. name: 'logs.host',
  289. fields: [
  290. {
  291. name: 'time',
  292. type: 'time',
  293. },
  294. {
  295. name: 'value',
  296. type: 'string',
  297. },
  298. ],
  299. },
  300. data: {
  301. values: [
  302. [1645208701000, 1645208702000],
  303. ['cbfa07e0e3bb 1', 'cbfa07e0e3bb 2'],
  304. ],
  305. },
  306. },
  307. {
  308. schema: {
  309. name: 'logs.message',
  310. fields: [
  311. {
  312. name: 'time',
  313. type: 'time',
  314. },
  315. {
  316. name: 'value',
  317. type: 'string',
  318. },
  319. ],
  320. },
  321. data: {
  322. values: [
  323. [1645208701000, 1645208702000],
  324. [
  325. 'Station softwareupdated[447]: Adding client 1',
  326. 'Station softwareupdated[447]: Adding client 2',
  327. ],
  328. ],
  329. },
  330. },
  331. {
  332. schema: {
  333. name: 'logs.path',
  334. fields: [
  335. {
  336. name: 'time',
  337. type: 'time',
  338. },
  339. {
  340. name: 'value',
  341. type: 'string',
  342. },
  343. ],
  344. },
  345. data: {
  346. values: [
  347. [1645208701000, 1645208702000],
  348. ['/var/log/host/install.log 1', '/var/log/host/install.log 2'],
  349. ],
  350. },
  351. },
  352. {
  353. schema: {
  354. name: 'textColumn',
  355. fields: [
  356. {
  357. name: 'time',
  358. type: 'time',
  359. },
  360. {
  361. name: 'value',
  362. type: 'string',
  363. },
  364. ],
  365. },
  366. data: {
  367. values: [
  368. [1645208701000, 1645208702000],
  369. ['text 1', 'text 2'],
  370. ],
  371. },
  372. },
  373. ],
  374. },
  375. },
  376. },
  377. } as FetchResponse);
  378. });
  379. ctx.ds = new InfluxDatasource(ctx.instanceSettings, templateSrv);
  380. ctx.ds.access = 'proxy';
  381. config.featureToggles.influxdbBackendMigration = true;
  382. response = await ctx.ds.annotationQuery(queryOptions);
  383. });
  384. it('should return annotation list', () => {
  385. expect(response.length).toBe(2);
  386. expect(response[0].time).toBe(1645208701000);
  387. expect(response[0].title).toBe('Station softwareupdated[447]: Adding client 1');
  388. expect(response[0].text).toBe('text 1');
  389. expect(response[0].tags[0]).toBe('cbfa07e0e3bb 1');
  390. expect(response[0].tags[1]).toBe('/var/log/host/install.log 1');
  391. expect(response[1].time).toBe(1645208702000);
  392. expect(response[1].title).toBe('Station softwareupdated[447]: Adding client 2');
  393. expect(response[1].text).toBe('text 2');
  394. expect(response[1].tags[0]).toBe('cbfa07e0e3bb 2');
  395. expect(response[1].tags[1]).toBe('/var/log/host/install.log 2');
  396. });
  397. });
  398. });