PanelQueryState.ts 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import {
  2. DataSourceApi,
  3. DataQueryRequest,
  4. PanelData,
  5. LoadingState,
  6. toLegacyResponseData,
  7. isSeriesData,
  8. toSeriesData,
  9. DataQueryError,
  10. } from '@grafana/ui';
  11. import { getProcessedSeriesData } from './PanelQueryRunner';
  12. import { getBackendSrv } from 'app/core/services/backend_srv';
  13. import isEqual from 'lodash/isEqual';
  14. export class PanelQueryState {
  15. // The current/last running request
  16. request = {
  17. startTime: 0,
  18. endTime: 1000, // Somethign not zero
  19. } as DataQueryRequest;
  20. // The best known state of data
  21. data = {
  22. state: LoadingState.NotStarted,
  23. series: [],
  24. } as PanelData;
  25. sendSeries = false;
  26. sendLegacy = false;
  27. // A promise for the running query
  28. private executor: Promise<PanelData> = {} as any;
  29. private rejector = (reason?: any) => {};
  30. private datasource: DataSourceApi = {} as any;
  31. isRunning() {
  32. return this.data.state === LoadingState.Loading; //
  33. }
  34. isSameQuery(ds: DataSourceApi, req: DataQueryRequest) {
  35. if (this.datasource !== this.datasource) {
  36. return false;
  37. }
  38. // For now just check that the targets look the same
  39. return isEqual(this.request.targets, req.targets);
  40. }
  41. getCurrentExecutor() {
  42. return this.executor;
  43. }
  44. cancel(reason: string) {
  45. const { request } = this;
  46. try {
  47. if (!request.endTime) {
  48. request.endTime = Date.now();
  49. this.rejector('Canceled:' + reason);
  50. }
  51. // Cancel any open HTTP request with the same ID
  52. if (request.requestId) {
  53. getBackendSrv().resolveCancelerIfExists(request.requestId);
  54. }
  55. } catch (err) {
  56. console.log('Error canceling request');
  57. }
  58. }
  59. execute(ds: DataSourceApi, req: DataQueryRequest): Promise<PanelData> {
  60. this.request = req;
  61. console.log('EXXXX', req);
  62. // Return early if there are no queries to run
  63. if (!req.targets.length) {
  64. console.log('No queries, so return early');
  65. this.request.endTime = Date.now();
  66. return Promise.resolve(
  67. (this.data = {
  68. state: LoadingState.Done,
  69. series: [], // Clear the data
  70. legacy: [],
  71. request: req,
  72. })
  73. );
  74. }
  75. // Set the loading state immediatly
  76. this.data.state = LoadingState.Loading;
  77. return (this.executor = new Promise<PanelData>((resolve, reject) => {
  78. this.rejector = reject;
  79. return ds
  80. .query(this.request)
  81. .then(resp => {
  82. this.request.endTime = Date.now();
  83. // Make sure we send something back -- called run() w/o subscribe!
  84. if (!(this.sendSeries || this.sendLegacy)) {
  85. this.sendSeries = true;
  86. }
  87. // Make sure the response is in a supported format
  88. const series = this.sendSeries ? getProcessedSeriesData(resp.data) : [];
  89. const legacy = this.sendLegacy
  90. ? resp.data.map(v => {
  91. if (isSeriesData(v)) {
  92. return toLegacyResponseData(v);
  93. }
  94. return v;
  95. })
  96. : undefined;
  97. resolve(
  98. (this.data = {
  99. state: LoadingState.Done,
  100. request: this.request,
  101. series,
  102. legacy,
  103. })
  104. );
  105. })
  106. .catch(err => {
  107. resolve(this.setError(err));
  108. });
  109. }));
  110. }
  111. /**
  112. * Make sure all requested formats exist on the data
  113. */
  114. getDataAfterCheckingFormats(): PanelData {
  115. const { data, sendLegacy, sendSeries } = this;
  116. if (sendLegacy && (!data.legacy || !data.legacy.length)) {
  117. data.legacy = data.series.map(v => toLegacyResponseData(v));
  118. }
  119. if (sendSeries && !data.series.length && data.legacy) {
  120. data.series = data.legacy.map(v => toSeriesData(v));
  121. }
  122. return this.data;
  123. }
  124. setError(err: any): PanelData {
  125. if (!this.request.endTime) {
  126. this.request.endTime = Date.now();
  127. }
  128. return (this.data = {
  129. ...this.data, // Keep any existing data
  130. state: LoadingState.Error,
  131. error: toDataQueryError(err),
  132. request: this.request,
  133. });
  134. }
  135. }
  136. export function toDataQueryError(err: any): DataQueryError {
  137. const error = (err || {}) as DataQueryError;
  138. if (!error.message) {
  139. if (typeof err === 'string' || err instanceof String) {
  140. return { message: err } as DataQueryError;
  141. }
  142. let message = 'Query error';
  143. if (error.message) {
  144. message = error.message;
  145. } else if (error.data && error.data.message) {
  146. message = error.data.message;
  147. } else if (error.data && error.data.error) {
  148. message = error.data.error;
  149. } else if (error.status) {
  150. message = `Query error: ${error.status} ${error.statusText}`;
  151. }
  152. error.message = message;
  153. }
  154. return error;
  155. }