QueryInspector.tsx 5.1 KB

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