QueryInspector.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  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. interface DsQuery {
  6. isLoading: boolean;
  7. response: {};
  8. }
  9. interface Props {
  10. panel: any;
  11. LoadingPlaceholder: 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. panel.events.on('refresh', this.onPanelRefresh);
  37. appEvents.on('ds-request-response', this.onDataSourceResponse);
  38. panel.refresh();
  39. }
  40. componentWillUnmount() {
  41. const { panel } = this.props;
  42. appEvents.off('ds-request-response', this.onDataSourceResponse);
  43. panel.events.off('refresh', this.onPanelRefresh);
  44. }
  45. handleMocking(response) {
  46. const { mockedResponse } = this.state;
  47. let mockedData;
  48. try {
  49. mockedData = JSON.parse(mockedResponse);
  50. } catch (err) {
  51. appEvents.emit('alert-error', ['R: Failed to parse mocked response']);
  52. return;
  53. }
  54. response.data = mockedData;
  55. }
  56. onPanelRefresh = () => {
  57. this.setState(prevState => ({
  58. ...prevState,
  59. dsQuery: {
  60. isLoading: true,
  61. response: {},
  62. },
  63. }));
  64. };
  65. onDataSourceResponse = (response: any = {}) => {
  66. // ignore if closed
  67. // if (!this.isOpen) {
  68. // return;
  69. // }
  70. if (this.state.isMocking) {
  71. this.handleMocking(response);
  72. return;
  73. }
  74. // this.isLoading = false;
  75. // data = _.cloneDeep(data);
  76. response = { ...response }; // clone
  77. if (response.headers) {
  78. delete response.headers;
  79. }
  80. if (response.config) {
  81. response.request = response.config;
  82. delete response.config;
  83. delete response.request.transformRequest;
  84. delete response.request.transformResponse;
  85. delete response.request.paramSerializer;
  86. delete response.request.jsonpCallbackParam;
  87. delete response.request.headers;
  88. delete response.request.requestId;
  89. delete response.request.inspect;
  90. delete response.request.retry;
  91. delete response.request.timeout;
  92. }
  93. if (response.data) {
  94. response.response = response.data;
  95. // if (response.status === 200) {
  96. // // if we are in error state, assume we automatically opened
  97. // // and auto close it again
  98. // if (this.hasError) {
  99. // this.hasError = false;
  100. // this.isOpen = false;
  101. // }
  102. // }
  103. delete response.data;
  104. delete response.status;
  105. delete response.statusText;
  106. delete response.$$config;
  107. }
  108. this.setState(prevState => ({
  109. ...prevState,
  110. dsQuery: {
  111. isLoading: false,
  112. response: response,
  113. },
  114. }));
  115. };
  116. setFormattedJson = formattedJson => {
  117. this.formattedJson = formattedJson;
  118. };
  119. getTextForClipboard = () => {
  120. return JSON.stringify(this.formattedJson, null, 2);
  121. };
  122. onClipboardSuccess = () => {
  123. appEvents.emit('alert-success', ['Content copied to clipboard']);
  124. };
  125. onToggleExpand = () => {
  126. this.setState(prevState => ({
  127. ...prevState,
  128. allNodesExpanded: !this.state.allNodesExpanded,
  129. }));
  130. };
  131. onToggleMocking = () => {
  132. this.setState(prevState => ({
  133. ...prevState,
  134. isMocking: !this.state.isMocking,
  135. }));
  136. };
  137. getNrOfOpenNodes = () => {
  138. if (this.state.allNodesExpanded === null) {
  139. return 3; // 3 is default, ie when state is null
  140. } else if (this.state.allNodesExpanded) {
  141. return 20;
  142. }
  143. return 1;
  144. };
  145. setMockedResponse = evt => {
  146. const mockedResponse = evt.target.value;
  147. this.setState(prevState => ({
  148. ...prevState,
  149. mockedResponse,
  150. }));
  151. };
  152. render() {
  153. const { response, isLoading } = this.state.dsQuery;
  154. const { LoadingPlaceholder } = this.props;
  155. const { allNodesExpanded, isMocking } = this.state;
  156. const openNodes = this.getNrOfOpenNodes();
  157. if (isLoading) {
  158. return <LoadingPlaceholder text="Loading query inspector..." />;
  159. }
  160. return (
  161. <>
  162. {/* <div className="query-troubleshooter__header">
  163. <a className="pointer" ng-click="ctrl.toggleMocking()">Mock Response</a>
  164. */}
  165. <div>
  166. <button className="btn btn-transparent btn-p-x-0 m-r-1" onClick={this.onToggleMocking}>
  167. Mock response
  168. </button>
  169. <button className="btn btn-transparent btn-p-x-0 m-r-1" onClick={this.onToggleExpand}>
  170. {allNodesExpanded ? (
  171. <>
  172. <i className="fa fa-minus-square-o" /> Collapse All
  173. </>
  174. ) : (
  175. <>
  176. <i className="fa fa-plus-square-o" /> Expand All
  177. </>
  178. )}
  179. </button>
  180. <CopyToClipboard
  181. className="btn btn-transparent btn-p-x-0"
  182. text={this.getTextForClipboard}
  183. onSuccess={this.onClipboardSuccess}
  184. >
  185. <>
  186. <i className="fa fa-clipboard" /> Copy to Clipboard
  187. </>
  188. </CopyToClipboard>
  189. </div>
  190. {!isMocking && <JSONFormatter json={response} open={openNodes} onDidRender={this.setFormattedJson} />}
  191. {isMocking && (
  192. <div className="query-troubleshooter__body">
  193. <div className="gf-form p-l-1 gf-form--v-stretch">
  194. <textarea
  195. className="gf-form-input"
  196. style={{ width: '95%' }}
  197. rows={10}
  198. onInput={this.setMockedResponse}
  199. placeholder="JSON"
  200. />
  201. {/* <textarea
  202. className="gf-form-input"
  203. style={{width: '95%'}}
  204. rows={10}
  205. ng-model="ctrl.mockedResponse"
  206. placeholder="JSON"></textarea> */}
  207. </div>
  208. </div>
  209. )}
  210. </>
  211. );
  212. }
  213. }