mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat: query troubleshooter
This commit is contained in:
parent
5513d3c9d1
commit
6ad1a396a5
58
public/app/core/components/collapse_box.ts
Normal file
58
public/app/core/components/collapse_box.ts
Normal file
@ -0,0 +1,58 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import coreModule from 'app/core/core_module';
|
||||
|
||||
const template = `
|
||||
<div class="collapse-box">
|
||||
<div class="collapse-box__header">
|
||||
<a class="collapse-box__header-title pointer" ng-click="ctrl.toggle()">
|
||||
<span class="fa fa-fw fa-caret-right" ng-hide="ctrl.isOpen"></span>
|
||||
<span class="fa fa-fw fa-caret-down" ng-hide="!ctrl.isOpen"></span>
|
||||
{{ctrl.title}}
|
||||
</a>
|
||||
<div class="collapse-box__header-actions" ng-transclude="actions"></div>
|
||||
</div>
|
||||
<div class="collapse-box__body" ng-transclude="body" ng-if="ctrl.isOpen">
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
export class CollapseBoxCtrl {
|
||||
isOpen: boolean;
|
||||
onOpen: () => void;
|
||||
|
||||
/** @ngInject **/
|
||||
constructor() {
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.isOpen = !this.isOpen;
|
||||
if (this.isOpen) {
|
||||
this.onOpen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function collapseBox() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller: CollapseBoxCtrl,
|
||||
bindToController: true,
|
||||
controllerAs: 'ctrl',
|
||||
scope: {
|
||||
"title": "@",
|
||||
"isOpen": "=?",
|
||||
"onOpen": "&"
|
||||
},
|
||||
transclude: {
|
||||
'actions': '?collapseBoxActions',
|
||||
'body': 'collapseBoxBody',
|
||||
},
|
||||
link: function(scope, elem, attrs) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
coreModule.directive('collapseBox', collapseBox);
|
@ -45,7 +45,7 @@ const _defaultConfig: JsonExplorerConfig = {
|
||||
* JsonExplorer allows you to render JSON objects in HTML with a
|
||||
* **collapsible** navigation.
|
||||
*/
|
||||
export default class JsonExplorer {
|
||||
export class JsonExplorer {
|
||||
|
||||
// Hold the open state after the toggler is used
|
||||
private _isOpen: boolean = null;
|
||||
@ -273,7 +273,7 @@ export default class JsonExplorer {
|
||||
*
|
||||
* @returns {HTMLDivElement}
|
||||
*/
|
||||
render(): HTMLDivElement {
|
||||
render(skipRoot = false): HTMLDivElement {
|
||||
|
||||
// construct the root element and assign it to this.element
|
||||
this.element = createElement('div', 'row');
|
||||
@ -371,7 +371,9 @@ export default class JsonExplorer {
|
||||
}
|
||||
|
||||
// append toggler and children elements to root element
|
||||
this.element.appendChild(togglerLink);
|
||||
if (!skipRoot) {
|
||||
this.element.appendChild(togglerLink);
|
||||
}
|
||||
this.element.appendChild(children);
|
||||
|
||||
// if formatter is set to be open call appendChildren
|
||||
|
@ -1,63 +0,0 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import coreModule from 'app/core/core_module';
|
||||
import JsonExplorer from './json_explorer/json_explorer';
|
||||
|
||||
|
||||
const template = `
|
||||
<div class="response-viewer">
|
||||
<div class="response-viewer-json"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
export function responseViewer() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
scope: {response: "="},
|
||||
link: function(scope, elem) {
|
||||
var jsonElem = elem.find('.response-viewer-json');
|
||||
|
||||
scope.$watch("response", newVal => {
|
||||
if (!newVal) {
|
||||
elem.empty();
|
||||
return;
|
||||
}
|
||||
|
||||
if (scope.response.headers) {
|
||||
delete scope.response.headers;
|
||||
}
|
||||
|
||||
if (scope.response.data) {
|
||||
scope.response.response = scope.response.data;
|
||||
delete scope.response.data;
|
||||
}
|
||||
|
||||
if (scope.response.config) {
|
||||
scope.response.request = scope.response.config;
|
||||
delete scope.response.config;
|
||||
delete scope.response.request.transformRequest;
|
||||
delete scope.response.request.transformResponse;
|
||||
delete scope.response.request.paramSerializer;
|
||||
delete scope.response.request.jsonpCallbackParam;
|
||||
delete scope.response.request.headers;
|
||||
delete scope.response.request.requestId;
|
||||
delete scope.response.request.inspect;
|
||||
delete scope.response.request.retry;
|
||||
delete scope.response.request.timeout;
|
||||
}
|
||||
|
||||
|
||||
const formatter = new JsonExplorer(scope.response, 2, {
|
||||
theme: 'dark',
|
||||
});
|
||||
|
||||
const html = formatter.render();
|
||||
jsonElem.html(html);
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
coreModule.directive('responseViewer', responseViewer);
|
@ -45,7 +45,8 @@ import {assignModelProperties} from './utils/model_utils';
|
||||
import {contextSrv} from './services/context_srv';
|
||||
import {KeybindingSrv} from './services/keybindingSrv';
|
||||
import {helpModal} from './components/help/help';
|
||||
import {responseViewer} from './components/response_viewer';
|
||||
import {collapseBox} from './components/collapse_box';
|
||||
import {JsonExplorer} from './components/json_explorer/json_explorer';
|
||||
|
||||
export {
|
||||
arrayJoin,
|
||||
@ -69,5 +70,6 @@ export {
|
||||
contextSrv,
|
||||
KeybindingSrv,
|
||||
helpModal,
|
||||
responseViewer,
|
||||
collapseBox,
|
||||
JsonExplorer,
|
||||
};
|
||||
|
@ -6,4 +6,5 @@ define([
|
||||
'./panel_editor_tab',
|
||||
'./query_editor_row',
|
||||
'./metrics_ds_selector',
|
||||
'./query_troubleshooter',
|
||||
], function () {});
|
||||
|
@ -8,10 +8,6 @@ var module = angular.module('grafana.directives');
|
||||
|
||||
var template = `
|
||||
|
||||
<div class="gf-form-group" ng-if="ctrl.showResponse">
|
||||
<response-viewer response="ctrl.responseData" />
|
||||
</div>
|
||||
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
@ -40,13 +36,6 @@ var template = `
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--offset-1">
|
||||
<button class="btn btn-inverse gf-form-btn" ng-click="ctrl.toggleShowResponse()" ng-show="ctrl.responseData">
|
||||
<i class="fa fa-binoculars"></i>
|
||||
Request & Response
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@ -81,24 +70,8 @@ export class MetricsDsSelectorCtrl {
|
||||
|
||||
this.dsSegment = uiSegmentSrv.newSegment({value: this.current.name, selectMode: true});
|
||||
this.mixedDsSegment = uiSegmentSrv.newSegment({value: 'Add Query', selectMode: true});
|
||||
|
||||
appEvents.on('ds-request-response', this.onRequestResponse.bind(this), $scope);
|
||||
appEvents.on('ds-request-error', this.onRequestError.bind(this), $scope);
|
||||
}
|
||||
|
||||
onRequestResponse(data) {
|
||||
this.responseData = data;
|
||||
}
|
||||
|
||||
toggleShowResponse() {
|
||||
this.showResponse = !this.showResponse;
|
||||
}
|
||||
|
||||
onRequestError(err) {
|
||||
this.responseData = err;
|
||||
this.responseData.isError = true;
|
||||
this.showResponse = true;
|
||||
}
|
||||
|
||||
getOptions(includeBuiltin) {
|
||||
return Promise.resolve(this.datasources.filter(value => {
|
||||
|
108
public/app/features/panel/query_troubleshooter.ts
Normal file
108
public/app/features/panel/query_troubleshooter.ts
Normal file
@ -0,0 +1,108 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import _ from 'lodash';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import {coreModule, JsonExplorer} from 'app/core/core';
|
||||
|
||||
const template = `
|
||||
<collapse-box title="Query Troubleshooter" is-open="ctrl.showResponse" on-open="ctrl.onOpen()">
|
||||
<collapse-box-actions>
|
||||
<a class="pointer"><i class="fa fa-clipboard"></i> Copy to clipboard</a>
|
||||
</collapse-box-actions>
|
||||
<collapse-box-body>
|
||||
<div class="query-troubleshooter-json"></div>
|
||||
</collapse-box-body>
|
||||
</collapse-box>
|
||||
`;
|
||||
|
||||
export class QueryTroubleshooterCtrl {
|
||||
responseData: any;
|
||||
showResponse: boolean;
|
||||
panelCtrl: any;
|
||||
renderJsonExplorer: (data) => void;
|
||||
|
||||
/** @ngInject **/
|
||||
constructor($scope, private $timeout) {
|
||||
appEvents.on('ds-request-response', this.onRequestResponse.bind(this), $scope);
|
||||
appEvents.on('ds-request-error', this.onRequestError.bind(this), $scope);
|
||||
}
|
||||
|
||||
onRequestResponse(data) {
|
||||
this.responseData = data;
|
||||
}
|
||||
|
||||
toggleShowResponse() {
|
||||
this.showResponse = !this.showResponse;
|
||||
}
|
||||
|
||||
onRequestError(err) {
|
||||
this.responseData = err;
|
||||
this.responseData.isError = true;
|
||||
this.showResponse = true;
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
if (!this.responseData) {
|
||||
console.log('no data');
|
||||
return;
|
||||
}
|
||||
|
||||
var data = this.responseData;
|
||||
if (data.headers) {
|
||||
delete data.headers;
|
||||
}
|
||||
|
||||
if (data.config) {
|
||||
data.request = data.config;
|
||||
delete data.config;
|
||||
delete data.request.transformRequest;
|
||||
delete data.request.transformResponse;
|
||||
delete data.request.paramSerializer;
|
||||
delete data.request.jsonpCallbackParam;
|
||||
delete data.request.headers;
|
||||
delete data.request.requestId;
|
||||
delete data.request.inspect;
|
||||
delete data.request.retry;
|
||||
delete data.request.timeout;
|
||||
}
|
||||
|
||||
if (data.data) {
|
||||
data.response = data.data;
|
||||
|
||||
delete data.data;
|
||||
delete data.status;
|
||||
delete data.statusText;
|
||||
delete data.$$config;
|
||||
}
|
||||
|
||||
this.$timeout(_.partial(this.renderJsonExplorer, data), 10);
|
||||
}
|
||||
}
|
||||
|
||||
export function queryTroubleshooter() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller: QueryTroubleshooterCtrl,
|
||||
bindToController: true,
|
||||
controllerAs: 'ctrl',
|
||||
scope: {
|
||||
panelCtrl: "="
|
||||
},
|
||||
link: function(scope, elem, attrs, ctrl) {
|
||||
|
||||
ctrl.renderJsonExplorer = function(data) {
|
||||
var jsonElem = elem.find('.query-troubleshooter-json');
|
||||
|
||||
const formatter = new JsonExplorer(data, 2, {
|
||||
theme: 'dark',
|
||||
});
|
||||
|
||||
const html = formatter.render(true);
|
||||
jsonElem.html(html);
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
coreModule.directive('queryTroubleshooter', queryTroubleshooter);
|
@ -1,4 +1,6 @@
|
||||
|
||||
<metrics-ds-selector panel-ctrl="ctrl"></metrics-ds-selector>
|
||||
|
||||
<div class="query-editor-rows gf-form-group">
|
||||
<div ng-repeat="target in ctrl.panel.targets" ng-class="{'gf-form-disabled': target.hide}">
|
||||
<rebuild-on-change property="ctrl.panel.datasource || target.datasource" show-null="true">
|
||||
@ -8,7 +10,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<metrics-ds-selector panel-ctrl="ctrl"></metrics-ds-selector>
|
||||
<query-troubleshooter panel-ctrl="ctrl"></query-troubleshooter>
|
||||
|
||||
<div class="gf-form-group">
|
||||
<rebuild-on-change property="ctrl.panel.datasource" show-null="true">
|
||||
|
@ -75,8 +75,8 @@
|
||||
@import "components/jsontree";
|
||||
@import "components/edit_sidemenu.scss";
|
||||
@import "components/row.scss";
|
||||
@import "components/response_viewer.scss";
|
||||
@import "components/json_explorer.scss";
|
||||
@import "components/collapse_box.scss";
|
||||
|
||||
// PAGES
|
||||
@import "pages/login";
|
||||
|
29
public/sass/components/_collapse_box.scss
Normal file
29
public/sass/components/_collapse_box.scss
Normal file
@ -0,0 +1,29 @@
|
||||
.collapse-box {
|
||||
margin-bottom: $spacer;
|
||||
}
|
||||
|
||||
.collapse-box__header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: $input-padding-y $input-padding-x;
|
||||
margin-right: $gf-form-margin;
|
||||
background-color: $input-bg;
|
||||
font-size: $font-size-sm;
|
||||
margin-right: $gf-form-margin;
|
||||
|
||||
border: $input-btn-border-width solid transparent;
|
||||
@include border-radius($label-border-radius-sm);
|
||||
}
|
||||
|
||||
.collapse-box__header-title {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.collapse-box__body {
|
||||
padding: $input-padding-y*2 $input-padding-x;
|
||||
background-color: $input-label-bg;
|
||||
display: block;
|
||||
margin-right: $gf-form-margin;
|
||||
border: $input-btn-border-width solid transparent;
|
||||
@include border-radius($label-border-radius-sm);
|
||||
}
|
@ -36,7 +36,7 @@
|
||||
|
||||
.json-formatter-string {
|
||||
color: $string-color;
|
||||
white-space: pre;
|
||||
white-space: normal;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.json-formatter-number { color: $number-color; }
|
||||
|
@ -1,6 +0,0 @@
|
||||
.response-viewer {
|
||||
background: $card-background;
|
||||
box-shadow: $card-shadow;
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
}
|
Loading…
Reference in New Issue
Block a user