QueryInspector.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import React, { PureComponent } from 'react';
  2. import appEvents from 'app/core/app_events';
  3. import { CopyToClipboard } from 'app/core/components/CopyToClipboard/CopyToClipboard';
  4. import { LoadingPlaceholder, JSONFormatter } from '@grafana/ui';
  5. interface DsQuery {
  6. isLoading: boolean;
  7. response: {};
  8. }
  9. interface Props {
  10. panel: any;
  11. }
  12. interface State {
  13. allNodesExpanded: boolean;
  14. isMocking: boolean;
  15. mockedResponse: string;
  16. dsQuery: DsQuery;
  17. }
  18. export class QueryInspector extends PureComponent<Props, State> {
  19. formattedJson: any;
  20. clipboard: any;
  21. constructor(props: Props) {
  22. super(props);
  23. this.state = {
  24. allNodesExpanded: null,
  25. isMocking: false,
  26. mockedResponse: '',
  27. dsQuery: {
  28. isLoading: false,
  29. response: {},
  30. },
  31. };
  32. }
  33. componentDidMount() {
  34. const { panel } = this.props;
  35. appEvents.on('ds-request-response', this.onDataSourceResponse);
  36. appEvents.on('ds-request-error', this.onRequestError);
  37. panel.events.on('refresh', this.onPanelRefresh);
  38. panel.refresh();
  39. }
  40. componentWillUnmount() {
  41. const { panel } = this.props;
  42. appEvents.off('ds-request-response', this.onDataSourceResponse);
  43. appEvents.on('ds-request-error', this.onRequestError);
  44. panel.events.off('refresh', this.onPanelRefresh);
  45. }
  46. handleMocking(response: any) {
  47. const { mockedResponse } = this.state;
  48. let mockedData;
  49. try {
  50. mockedData = JSON.parse(mockedResponse);
  51. } catch (err) {
  52. appEvents.emit('alert-error', ['R: Failed to parse mocked response']);
  53. return;
  54. }
  55. response.data = mockedData;
  56. }
  57. onPanelRefresh = () => {
  58. this.setState(prevState => ({
  59. ...prevState,
  60. dsQuery: {
  61. isLoading: true,
  62. response: {},
  63. },
  64. }));
  65. };
  66. onRequestError = (err: any) => {
  67. this.onDataSourceResponse(err);
  68. };
  69. onDataSourceResponse = (response: any = {}) => {
  70. if (this.state.isMocking) {
  71. this.handleMocking(response);
  72. return;
  73. }
  74. response = { ...response }; // clone - dont modify the response
  75. if (response.headers) {
  76. delete response.headers;
  77. }
  78. if (response.config) {
  79. response.request = response.config;
  80. delete response.config;
  81. delete response.request.transformRequest;
  82. delete response.request.transformResponse;
  83. delete response.request.paramSerializer;
  84. delete response.request.jsonpCallbackParam;
  85. delete response.request.headers;
  86. delete response.request.requestId;
  87. delete response.request.inspect;
  88. delete response.request.retry;
  89. delete response.request.timeout;
  90. }
  91. if (response.data) {
  92. response.response = response.data;
  93. delete response.data;
  94. delete response.status;
  95. delete response.statusText;
  96. delete response.$$config;
  97. }
  98. this.setState(prevState => ({
  99. ...prevState,
  100. dsQuery: {
  101. isLoading: false,
  102. response: response,
  103. },
  104. }));
  105. };
  106. setFormattedJson = (formattedJson: any) => {
  107. this.formattedJson = formattedJson;
  108. };
  109. getTextForClipboard = () => {
  110. return JSON.stringify(this.formattedJson, null, 2);
  111. };
  112. onClipboardSuccess = () => {
  113. appEvents.emit('alert-success', ['Content copied to clipboard']);
  114. };
  115. onToggleExpand = () => {
  116. this.setState(prevState => ({
  117. ...prevState,
  118. allNodesExpanded: !this.state.allNodesExpanded,
  119. }));
  120. };
  121. onToggleMocking = () => {
  122. this.setState(prevState => ({
  123. ...prevState,
  124. isMocking: !this.state.isMocking,
  125. }));
  126. };
  127. getNrOfOpenNodes = () => {
  128. if (this.state.allNodesExpanded === null) {
  129. return 3; // 3 is default, ie when state is null
  130. } else if (this.state.allNodesExpanded) {
  131. return 20;
  132. }
  133. return 1;
  134. };
  135. setMockedResponse = (evt: any) => {
  136. const mockedResponse = evt.target.value;
  137. this.setState(prevState => ({
  138. ...prevState,
  139. mockedResponse,
  140. }));
  141. };
  142. renderExpandCollapse = () => {
  143. const { allNodesExpanded } = this.state;
  144. const collapse = (
  145. <>
  146. <i className="fa fa-minus-square-o" /> Collapse All
  147. </>
  148. );
  149. const expand = (
  150. <>
  151. <i className="fa fa-plus-square-o" /> Expand All
  152. </>
  153. );
  154. return allNodesExpanded ? collapse : expand;
  155. };
  156. render() {
  157. const { response, isLoading } = this.state.dsQuery;
  158. const openNodes = this.getNrOfOpenNodes();
  159. if (isLoading) {
  160. return <LoadingPlaceholder text="Loading query inspector..." />;
  161. }
  162. return (
  163. <>
  164. <div className="pull-right">
  165. <button className="btn btn-transparent btn-p-x-0 m-r-1" onClick={this.onToggleExpand}>
  166. {this.renderExpandCollapse()}
  167. </button>
  168. <CopyToClipboard
  169. className="btn btn-transparent btn-p-x-0"
  170. text={this.getTextForClipboard}
  171. onSuccess={this.onClipboardSuccess}
  172. >
  173. <i className="fa fa-clipboard" /> Copy to Clipboard
  174. </CopyToClipboard>
  175. </div>
  176. <JSONFormatter json={response} open={openNodes} onDidRender={this.setFormattedJson} />
  177. </>
  178. );
  179. }
  180. }