1import { DataQuery, toDataFrameDTO, DataFrame } from '@grafana/data'; 2import { FetchError, FetchResponse } from 'src/services'; 3import { BackendDataSourceResponse, toDataQueryResponse, toTestingStatus } from './queryResponse'; 4 5const resp = ({ 6 data: { 7 results: { 8 A: { 9 frames: [ 10 { 11 schema: { 12 refId: 'A', 13 fields: [ 14 { name: 'time', type: 'time', typeInfo: { frame: 'time.Time', nullable: true } }, 15 { name: 'A-series', type: 'number', typeInfo: { frame: 'float64', nullable: true } }, 16 ], 17 }, 18 data: { 19 values: [ 20 [1611767228473, 1611767240473, 1611767252473, 1611767264473, 1611767276473, 1611767288473], 21 [1, 20, 90, 30, 5, 0], 22 ], 23 }, 24 }, 25 ], 26 }, 27 B: { 28 frames: [ 29 { 30 schema: { 31 refId: 'B', 32 fields: [ 33 { name: 'time', type: 'time', typeInfo: { frame: 'time.Time', nullable: true } }, 34 { name: 'B-series', type: 'number', typeInfo: { frame: 'float64', nullable: true } }, 35 ], 36 }, 37 data: { 38 values: [ 39 [1611767228473, 1611767240473, 1611767252473, 1611767264473, 1611767276473, 1611767288473], 40 [1, 20, 90, 30, 5, 0], 41 ], 42 }, 43 }, 44 ], 45 }, 46 }, 47 }, 48} as any) as FetchResponse<BackendDataSourceResponse>; 49 50const resWithError = ({ 51 data: { 52 results: { 53 A: { 54 error: 'Hello Error', 55 frames: [ 56 { 57 schema: { 58 fields: [{ name: 'numbers', type: 'number' }], 59 meta: { 60 notices: [ 61 { 62 severity: 2, 63 text: 'Text', 64 }, 65 ], 66 }, 67 }, 68 data: { 69 values: [[1, 3]], 70 }, 71 }, 72 ], 73 }, 74 }, 75 }, 76} as any) as FetchResponse<BackendDataSourceResponse>; 77 78const emptyResults = { 79 data: { results: { '': { refId: '' } } }, 80}; 81 82describe('Query Response parser', () => { 83 test('should parse output with dataframe', () => { 84 const res = toDataQueryResponse(resp); 85 const frames = res.data; 86 expect(frames).toHaveLength(2); 87 expect(frames[0].refId).toEqual('A'); 88 expect(frames[1].refId).toEqual('B'); 89 90 const norm = frames.map((f) => toDataFrameDTO(f)); 91 expect(norm).toMatchInlineSnapshot(` 92 Array [ 93 Object { 94 "fields": Array [ 95 Object { 96 "config": Object {}, 97 "labels": undefined, 98 "name": "time", 99 "type": "time", 100 "values": Array [ 101 1611767228473, 102 1611767240473, 103 1611767252473, 104 1611767264473, 105 1611767276473, 106 1611767288473, 107 ], 108 }, 109 Object { 110 "config": Object {}, 111 "labels": undefined, 112 "name": "A-series", 113 "type": "number", 114 "values": Array [ 115 1, 116 20, 117 90, 118 30, 119 5, 120 0, 121 ], 122 }, 123 ], 124 "meta": undefined, 125 "name": undefined, 126 "refId": "A", 127 }, 128 Object { 129 "fields": Array [ 130 Object { 131 "config": Object {}, 132 "labels": undefined, 133 "name": "time", 134 "type": "time", 135 "values": Array [ 136 1611767228473, 137 1611767240473, 138 1611767252473, 139 1611767264473, 140 1611767276473, 141 1611767288473, 142 ], 143 }, 144 Object { 145 "config": Object {}, 146 "labels": undefined, 147 "name": "B-series", 148 "type": "number", 149 "values": Array [ 150 1, 151 20, 152 90, 153 30, 154 5, 155 0, 156 ], 157 }, 158 ], 159 "meta": undefined, 160 "name": undefined, 161 "refId": "B", 162 }, 163 ] 164 `); 165 }); 166 167 test('should parse output with dataframe in order of queries', () => { 168 const queries: DataQuery[] = [{ refId: 'B' }, { refId: 'A' }]; 169 const res = toDataQueryResponse(resp, queries); 170 const frames = res.data; 171 expect(frames).toHaveLength(2); 172 expect(frames[0].refId).toEqual('B'); 173 expect(frames[1].refId).toEqual('A'); 174 175 const norm = frames.map((f) => toDataFrameDTO(f)); 176 expect(norm).toMatchInlineSnapshot(` 177 Array [ 178 Object { 179 "fields": Array [ 180 Object { 181 "config": Object {}, 182 "labels": undefined, 183 "name": "time", 184 "type": "time", 185 "values": Array [ 186 1611767228473, 187 1611767240473, 188 1611767252473, 189 1611767264473, 190 1611767276473, 191 1611767288473, 192 ], 193 }, 194 Object { 195 "config": Object {}, 196 "labels": undefined, 197 "name": "B-series", 198 "type": "number", 199 "values": Array [ 200 1, 201 20, 202 90, 203 30, 204 5, 205 0, 206 ], 207 }, 208 ], 209 "meta": undefined, 210 "name": undefined, 211 "refId": "B", 212 }, 213 Object { 214 "fields": Array [ 215 Object { 216 "config": Object {}, 217 "labels": undefined, 218 "name": "time", 219 "type": "time", 220 "values": Array [ 221 1611767228473, 222 1611767240473, 223 1611767252473, 224 1611767264473, 225 1611767276473, 226 1611767288473, 227 ], 228 }, 229 Object { 230 "config": Object {}, 231 "labels": undefined, 232 "name": "A-series", 233 "type": "number", 234 "values": Array [ 235 1, 236 20, 237 90, 238 30, 239 5, 240 0, 241 ], 242 }, 243 ], 244 "meta": undefined, 245 "name": undefined, 246 "refId": "A", 247 }, 248 ] 249 `); 250 }); 251 252 test('processEmptyResults', () => { 253 const frames = toDataQueryResponse(emptyResults).data; 254 expect(frames.length).toEqual(0); 255 }); 256 257 test('keeps query order', () => { 258 const resp = { 259 data: { 260 results: { 261 X: { 262 series: [{ name: 'Requests/s', points: [[13.594958983547151, 1611839862951]] }] as any, 263 }, 264 B: { 265 series: [{ name: 'Requests/s', points: [[13.594958983547151, 1611839862951]] }] as any, 266 }, 267 A: { 268 series: [{ name: 'Requests/s', points: [[13.594958983547151, 1611839862951]] }] as any, 269 }, 270 }, 271 }, 272 }; 273 274 const queries: DataQuery[] = [{ refId: 'A' }, { refId: 'B' }]; 275 276 const ids = (toDataQueryResponse(resp, queries).data as DataFrame[]).map((f) => f.refId); 277 expect(ids).toEqual(['A', 'B']); 278 }); 279 280 test('resultWithError', () => { 281 // Generated from: 282 // qdr.Responses[q.GetRefID()] = backend.DataResponse{ 283 // Error: fmt.Errorf("an Error: %w", fmt.Errorf("another error")), 284 // Frames: data.Frames{ 285 // { 286 // Fields: data.Fields{data.NewField("numbers", nil, []float64{1, 3})}, 287 // Meta: &data.FrameMeta{ 288 // Notices: []data.Notice{ 289 // { 290 // Severity: data.NoticeSeverityError, 291 // Text: "Text", 292 // }, 293 // }, 294 // }, 295 // }, 296 // }, 297 // } 298 const res = toDataQueryResponse(resWithError); 299 expect(res.error).toMatchInlineSnapshot(` 300 Object { 301 "message": "Hello Error", 302 "refId": "A", 303 } 304 `); 305 306 const norm = res.data.map((f) => toDataFrameDTO(f)); 307 expect(norm).toMatchInlineSnapshot(` 308 Array [ 309 Object { 310 "fields": Array [ 311 Object { 312 "config": Object {}, 313 "labels": undefined, 314 "name": "numbers", 315 "type": "number", 316 "values": Array [ 317 1, 318 3, 319 ], 320 }, 321 ], 322 "meta": Object { 323 "notices": Array [ 324 Object { 325 "severity": 2, 326 "text": "Text", 327 }, 328 ], 329 }, 330 "name": undefined, 331 "refId": "A", 332 }, 333 ] 334 `); 335 }); 336 337 describe('should convert to TestingStatus', () => { 338 test('from api/ds/query generic errors', () => { 339 const result = toTestingStatus({ status: 500, data: { message: 'message', error: 'error' } } as FetchError); 340 expect(result).toMatchObject({ 341 status: 'error', 342 message: 'message', 343 details: { message: 'error' }, 344 }); 345 }); 346 test('from api/ds/query result errors', () => { 347 const result = toTestingStatus({ 348 status: 400, 349 data: { 350 results: { 351 A: { 352 error: 'error', 353 }, 354 }, 355 }, 356 } as FetchError); 357 expect(result).toMatchObject({ 358 status: 'error', 359 message: 'error', 360 }); 361 }); 362 test('unknown errors', () => { 363 expect(() => { 364 toTestingStatus({ status: 503, data: 'Fatal Error' } as FetchError); 365 }).toThrow(); 366 367 expect(() => { 368 toTestingStatus({ status: 503, data: {} } as FetchError); 369 }).toThrow(); 370 371 expect(() => { 372 toTestingStatus({ status: 503 } as FetchError); 373 }).toThrow(); 374 }); 375 }); 376}); 377