|
|
@@ -3,12 +3,21 @@ import { JSONFormatter } from 'app/core/components/JSONFormatter/JSONFormatter';
|
|
|
import appEvents from 'app/core/app_events';
|
|
|
import { CopyToClipboard } from 'app/core/components/CopyToClipboard/CopyToClipboard';
|
|
|
|
|
|
+interface DsQuery {
|
|
|
+ isLoading: boolean;
|
|
|
+ response: {};
|
|
|
+}
|
|
|
+
|
|
|
interface Props {
|
|
|
- response: any;
|
|
|
+ panel: any;
|
|
|
+ LoadingPlaceholder: any;
|
|
|
}
|
|
|
|
|
|
interface State {
|
|
|
allNodesExpanded: boolean;
|
|
|
+ isMocking: boolean;
|
|
|
+ mockedResponse: string;
|
|
|
+ dsQuery: DsQuery;
|
|
|
}
|
|
|
|
|
|
export class QueryInspector extends PureComponent<Props, State> {
|
|
|
@@ -17,12 +26,112 @@ export class QueryInspector extends PureComponent<Props, State> {
|
|
|
|
|
|
constructor(props) {
|
|
|
super(props);
|
|
|
-
|
|
|
this.state = {
|
|
|
allNodesExpanded: null,
|
|
|
+ isMocking: false,
|
|
|
+ mockedResponse: '',
|
|
|
+ dsQuery: {
|
|
|
+ isLoading: false,
|
|
|
+ response: {},
|
|
|
+ },
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+ componentDidMount() {
|
|
|
+ const { panel } = this.props;
|
|
|
+ panel.events.on('refresh', this.onPanelRefresh);
|
|
|
+ appEvents.on('ds-request-response', this.onDataSourceResponse);
|
|
|
+ panel.refresh();
|
|
|
+ }
|
|
|
+
|
|
|
+ componentWillUnmount() {
|
|
|
+ const { panel } = this.props;
|
|
|
+ appEvents.off('ds-request-response', this.onDataSourceResponse);
|
|
|
+ panel.events.off('refresh', this.onPanelRefresh);
|
|
|
+ }
|
|
|
+
|
|
|
+ handleMocking(response) {
|
|
|
+ const { mockedResponse } = this.state;
|
|
|
+ let mockedData;
|
|
|
+ try {
|
|
|
+ mockedData = JSON.parse(mockedResponse);
|
|
|
+ } catch (err) {
|
|
|
+ appEvents.emit('alert-error', ['R: Failed to parse mocked response']);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ response.data = mockedData;
|
|
|
+ }
|
|
|
+
|
|
|
+ onPanelRefresh = () => {
|
|
|
+ this.setState(prevState => ({
|
|
|
+ ...prevState,
|
|
|
+ dsQuery: {
|
|
|
+ isLoading: true,
|
|
|
+ response: {},
|
|
|
+ },
|
|
|
+ }));
|
|
|
+ };
|
|
|
+
|
|
|
+ onDataSourceResponse = (response: any = {}) => {
|
|
|
+ // ignore if closed
|
|
|
+ // if (!this.isOpen) {
|
|
|
+ // return;
|
|
|
+ // }
|
|
|
+
|
|
|
+ if (this.state.isMocking) {
|
|
|
+ this.handleMocking(response);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // this.isLoading = false;
|
|
|
+ // data = _.cloneDeep(data);
|
|
|
+ response = { ...response }; // clone
|
|
|
+
|
|
|
+ if (response.headers) {
|
|
|
+ delete response.headers;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (response.config) {
|
|
|
+ response.request = response.config;
|
|
|
+ delete response.config;
|
|
|
+ delete response.request.transformRequest;
|
|
|
+ delete response.request.transformResponse;
|
|
|
+ delete response.request.paramSerializer;
|
|
|
+ delete response.request.jsonpCallbackParam;
|
|
|
+ delete response.request.headers;
|
|
|
+ delete response.request.requestId;
|
|
|
+ delete response.request.inspect;
|
|
|
+ delete response.request.retry;
|
|
|
+ delete response.request.timeout;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (response.data) {
|
|
|
+ response.response = response.data;
|
|
|
+
|
|
|
+ // if (response.status === 200) {
|
|
|
+ // // if we are in error state, assume we automatically opened
|
|
|
+ // // and auto close it again
|
|
|
+ // if (this.hasError) {
|
|
|
+ // this.hasError = false;
|
|
|
+ // this.isOpen = false;
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+
|
|
|
+ delete response.data;
|
|
|
+ delete response.status;
|
|
|
+ delete response.statusText;
|
|
|
+ delete response.$$config;
|
|
|
+ }
|
|
|
+ this.setState(prevState => ({
|
|
|
+ ...prevState,
|
|
|
+ dsQuery: {
|
|
|
+ isLoading: false,
|
|
|
+ response: response,
|
|
|
+ },
|
|
|
+ }));
|
|
|
+ };
|
|
|
+
|
|
|
setFormattedJson = formattedJson => {
|
|
|
this.formattedJson = formattedJson;
|
|
|
};
|
|
|
@@ -42,56 +151,92 @@ export class QueryInspector extends PureComponent<Props, State> {
|
|
|
}));
|
|
|
};
|
|
|
|
|
|
+ onToggleMocking = () => {
|
|
|
+ this.setState(prevState => ({
|
|
|
+ ...prevState,
|
|
|
+ isMocking: !this.state.isMocking,
|
|
|
+ }));
|
|
|
+ };
|
|
|
+
|
|
|
getNrOfOpenNodes = () => {
|
|
|
if (this.state.allNodesExpanded === null) {
|
|
|
- return 3;
|
|
|
+ return 3; // 3 is default, ie when state is null
|
|
|
} else if (this.state.allNodesExpanded) {
|
|
|
return 20;
|
|
|
}
|
|
|
return 1;
|
|
|
};
|
|
|
|
|
|
+ setMockedResponse = evt => {
|
|
|
+ const mockedResponse = evt.target.value;
|
|
|
+ this.setState(prevState => ({
|
|
|
+ ...prevState,
|
|
|
+ mockedResponse,
|
|
|
+ }));
|
|
|
+ };
|
|
|
+
|
|
|
render() {
|
|
|
- const { response } = this.props;
|
|
|
- const { allNodesExpanded } = this.state;
|
|
|
+ const { response, isLoading } = this.state.dsQuery;
|
|
|
+ const { LoadingPlaceholder } = this.props;
|
|
|
+ const { allNodesExpanded, isMocking } = this.state;
|
|
|
const openNodes = this.getNrOfOpenNodes();
|
|
|
+
|
|
|
+ if (isLoading) {
|
|
|
+ return <LoadingPlaceholder text="Loading query inspector..." />;
|
|
|
+ }
|
|
|
+
|
|
|
return (
|
|
|
<>
|
|
|
{/* <div className="query-troubleshooter__header">
|
|
|
<a className="pointer" ng-click="ctrl.toggleMocking()">Mock Response</a>
|
|
|
- <a className="pointer" ng-click="ctrl.toggleExpand()" ng-hide="ctrl.allNodesExpanded">
|
|
|
- <i className="fa fa-plus-square-o"></i> Expand All
|
|
|
- </a>
|
|
|
- <a className="pointer ng-hide" ng-click="ctrl.toggleExpand()" ng-show="ctrl.allNodesExpanded">
|
|
|
- <i className="fa fa-minus-square-o"></i> Collapse All
|
|
|
- </a>
|
|
|
- <a className="pointer ng-isolate-scope" clipboard-button="ctrl.getClipboardText()"><i className="fa fa-clipboard"></i> Copy to Clipboard</a>
|
|
|
-
|
|
|
- </div> */}
|
|
|
- {/* <button onClick={this.copyToClipboard}>Copy</button>
|
|
|
- <button ref={this.copyButtonRef}>Copy2</button> */}
|
|
|
- <button className="btn btn-transparent" onClick={this.onToggleExpand}>
|
|
|
- {allNodesExpanded ? (
|
|
|
- <>
|
|
|
- <i className="fa fa-minus-square-o" /> Collapse All
|
|
|
- </>
|
|
|
- ) : (
|
|
|
+ */}
|
|
|
+ <div>
|
|
|
+ <button className="btn btn-transparent btn-p-x-0 m-r-1" onClick={this.onToggleMocking}>
|
|
|
+ Mock response
|
|
|
+ </button>
|
|
|
+ <button className="btn btn-transparent btn-p-x-0 m-r-1" onClick={this.onToggleExpand}>
|
|
|
+ {allNodesExpanded ? (
|
|
|
+ <>
|
|
|
+ <i className="fa fa-minus-square-o" /> Collapse All
|
|
|
+ </>
|
|
|
+ ) : (
|
|
|
+ <>
|
|
|
+ <i className="fa fa-plus-square-o" /> Expand All
|
|
|
+ </>
|
|
|
+ )}
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <CopyToClipboard
|
|
|
+ className="btn btn-transparent btn-p-x-0"
|
|
|
+ text={this.getTextForClipboard}
|
|
|
+ onSuccess={this.onClipboardSuccess}
|
|
|
+ >
|
|
|
<>
|
|
|
- <i className="fa fa-plus-square-o" /> Expand All
|
|
|
+ <i className="fa fa-clipboard" /> Copy to Clipboard
|
|
|
</>
|
|
|
- )}
|
|
|
- </button>
|
|
|
-
|
|
|
- <CopyToClipboard
|
|
|
- className="btn btn-transparent"
|
|
|
- text={this.getTextForClipboard}
|
|
|
- onSuccess={this.onClipboardSuccess}
|
|
|
- >
|
|
|
- <>
|
|
|
- <i className="fa fa-clipboard" /> Copy to Clipboard
|
|
|
- </>
|
|
|
- </CopyToClipboard>
|
|
|
- <JSONFormatter json={response} open={openNodes} onDidRender={this.setFormattedJson} />
|
|
|
+ </CopyToClipboard>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {!isMocking && <JSONFormatter json={response} open={openNodes} onDidRender={this.setFormattedJson} />}
|
|
|
+ {isMocking && (
|
|
|
+ <div className="query-troubleshooter__body">
|
|
|
+ <div className="gf-form p-l-1 gf-form--v-stretch">
|
|
|
+ <textarea
|
|
|
+ className="gf-form-input"
|
|
|
+ style={{ width: '95%' }}
|
|
|
+ rows={10}
|
|
|
+ onInput={this.setMockedResponse}
|
|
|
+ placeholder="JSON"
|
|
|
+ />
|
|
|
+ {/* <textarea
|
|
|
+ className="gf-form-input"
|
|
|
+ style={{width: '95%'}}
|
|
|
+ rows={10}
|
|
|
+ ng-model="ctrl.mockedResponse"
|
|
|
+ placeholder="JSON"></textarea> */}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
</>
|
|
|
);
|
|
|
}
|