Browse Source

feat: query troubleshooter

Torkel Ödegaard 8 years ago
parent
commit
6ad1a396a5

+ 58 - 0
public/app/core/components/collapse_box.ts

@@ -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);

+ 5 - 3
public/app/core/components/json_explorer/json_explorer.ts

@@ -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

+ 0 - 63
public/app/core/components/response_viewer.ts

@@ -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);

+ 4 - 2
public/app/core/core.ts

@@ -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,
 };

+ 1 - 0
public/app/features/panel/all.js

@@ -6,4 +6,5 @@ define([
   './panel_editor_tab',
   './query_editor_row',
   './metrics_ds_selector',
+  './query_troubleshooter',
 ], function () {});

+ 0 - 27
public/app/features/panel/metrics_ds_selector.ts

@@ -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>&nbsp;
-        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 - 0
public/app/features/panel/query_troubleshooter.ts

@@ -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);

+ 3 - 1
public/app/partials/metrics.html

@@ -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">

+ 1 - 1
public/sass/_grafana.scss

@@ -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 - 0
public/sass/components/_collapse_box.scss

@@ -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);
+}

+ 1 - 1
public/sass/components/_json_explorer.scss

@@ -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; }

+ 0 - 6
public/sass/components/_response_viewer.scss

@@ -1,6 +0,0 @@
-.response-viewer {
-  background: $card-background;
-  box-shadow: $card-shadow;
-  padding: 1rem;
-  border-radius: 4px;
-}