PanelQueryState.ts 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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. // Return early if there are no queries to run
  62. if (!req.targets.length) {
  63. console.log('No queries, so return early');
  64. this.request.endTime = Date.now();
  65. return Promise.resolve(
  66. (this.data = {
  67. state: LoadingState.Done,
  68. series: [], // Clear the data
  69. legacy: [],
  70. request: req,
  71. })
  72. );
  73. }
  74. // Set the loading state immediatly
  75. this.data.state = LoadingState.Loading;
  76. return (this.executor = new Promise<PanelData>((resolve, reject) => {
  77. this.rejector = reject;
  78. return ds
  79. .query(this.request)
  80. .then(resp => {
  81. this.request.endTime = Date.now();
  82. // Make sure we send something back -- called run() w/o subscribe!
  83. if (!(this.sendSeries || this.sendLegacy)) {
  84. this.sendSeries = true;
  85. }
  86. // Make sure the response is in a supported format
  87. const series = this.sendSeries ? getProcessedSeriesData(resp.data) : [];
  88. const legacy = this.sendLegacy
  89. ? resp.data.map(v => {
  90. if (isSeriesData(v)) {
  91. return toLegacyResponseData(v);
  92. }
  93. return v;
  94. })
  95. : undefined;
  96. resolve(
  97. (this.data = {
  98. state: LoadingState.Done,
  99. request: this.request,
  100. series,
  101. legacy,
  102. })
  103. );
  104. })
  105. .catch(err => {
  106. resolve(this.setError(err));
  107. });
  108. }));
  109. }
  110. /**
  111. * Make sure all requested formats exist on the data
  112. */
  113. getDataAfterCheckingFormats(): PanelData {
  114. const { data, sendLegacy, sendSeries } = this;
  115. if (sendLegacy && (!data.legacy || !data.legacy.length)) {
  116. data.legacy = data.series.map(v => toLegacyResponseData(v));
  117. }
  118. if (sendSeries && !data.series.length && data.legacy) {
  119. data.series = data.legacy.map(v => toSeriesData(v));
  120. }
  121. return this.data;
  122. }
  123. setError(err: any): PanelData {
  124. if (!this.request.endTime) {
  125. this.request.endTime = Date.now();
  126. }
  127. return (this.data = {
  128. ...this.data, // Keep any existing data
  129. state: LoadingState.Error,
  130. error: toDataQueryError(err),
  131. request: this.request,
  132. });
  133. }
  134. }
  135. export function toDataQueryError(err: any): DataQueryError {
  136. const error = (err || {}) as DataQueryError;
  137. if (!error.message) {
  138. if (typeof err === 'string' || err instanceof String) {
  139. return { message: err } as DataQueryError;
  140. }
  141. let message = 'Query error';
  142. if (error.message) {
  143. message = error.message;
  144. } else if (error.data && error.data.message) {
  145. message = error.data.message;
  146. } else if (error.data && error.data.error) {
  147. message = error.data.error;
  148. } else if (error.status) {
  149. message = `Query error: ${error.status} ${error.statusText}`;
  150. }
  151. error.message = message;
  152. }
  153. return error;
  154. }