query_troubleshooter.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. ///<reference path="../../headers/common.d.ts" />
  2. import _ from 'lodash';
  3. import appEvents from 'app/core/app_events';
  4. import {coreModule, JsonExplorer} from 'app/core/core';
  5. const template = `
  6. <div class="query-troubleshooter" ng-if="ctrl.isOpen">
  7. <div class="query-troubleshooter__header">
  8. <a class="pointer" ng-click="ctrl.toggleMocking()">Mock Response</a>
  9. <a class="pointer" ng-click="ctrl.toggleExpand()" ng-hide="ctrl.allNodesExpanded">
  10. <i class="fa fa-plus-square-o"></i> Expand All
  11. </a>
  12. <a class="pointer" ng-click="ctrl.toggleExpand()" ng-show="ctrl.allNodesExpanded">
  13. <i class="fa fa-minus-square-o"></i> Collapse All
  14. </a>
  15. <a class="pointer" clipboard-button="ctrl.getClipboardText()"><i class="fa fa-clipboard"></i> Copy to Clipboard</a>
  16. </div>
  17. <div class="query-troubleshooter__body" ng-hide="ctrl.isMocking">
  18. <i class="fa fa-spinner fa-spin" ng-show="ctrl.isLoading"></i>
  19. <div class="query-troubleshooter-json"></div>
  20. </div>
  21. <div class="query-troubleshooter__body" ng-show="ctrl.isMocking">
  22. <div class="gf-form p-l-1 gf-form--v-stretch">
  23. <textarea class="gf-form-input" style="width: 95%" rows="10" ng-model="ctrl.mockedResponse" placeholder="JSON"></textarea>
  24. </div>
  25. </div>
  26. </div>
  27. `;
  28. export class QueryTroubleshooterCtrl {
  29. isOpen: any;
  30. isLoading: boolean;
  31. showResponse: boolean;
  32. panelCtrl: any;
  33. renderJsonExplorer: (data) => void;
  34. onRequestErrorEventListener: any;
  35. onRequestResponseEventListener: any;
  36. hasError: boolean;
  37. allNodesExpanded: boolean;
  38. isMocking: boolean;
  39. mockedResponse: string;
  40. jsonExplorer: JsonExplorer;
  41. /** @ngInject **/
  42. constructor($scope, private $timeout) {
  43. this.onRequestErrorEventListener = this.onRequestError.bind(this);
  44. this.onRequestResponseEventListener = this.onRequestResponse.bind(this);
  45. appEvents.on('ds-request-response', this.onRequestResponseEventListener);
  46. appEvents.on('ds-request-error', this.onRequestErrorEventListener);
  47. $scope.$on('$destroy', this.removeEventsListeners.bind(this));
  48. $scope.$watch('ctrl.isOpen', this.stateChanged.bind(this));
  49. }
  50. removeEventsListeners() {
  51. appEvents.off('ds-request-response', this.onRequestResponseEventListener);
  52. appEvents.off('ds-request-error', this.onRequestErrorEventListener);
  53. }
  54. toggleMocking() {
  55. this.isMocking = !this.isMocking;
  56. }
  57. onRequestError(err) {
  58. // ignore if closed
  59. if (!this.isOpen) {
  60. return;
  61. }
  62. this.isOpen = true;
  63. this.hasError = true;
  64. this.onRequestResponse(err);
  65. }
  66. stateChanged() {
  67. if (this.isOpen) {
  68. this.panelCtrl.refresh();
  69. this.isLoading = true;
  70. }
  71. }
  72. getClipboardText(): string {
  73. if (this.jsonExplorer) {
  74. return JSON.stringify(this.jsonExplorer.json, null, 2);
  75. }
  76. return '';
  77. }
  78. handleMocking(data) {
  79. var mockedData;
  80. try {
  81. mockedData = JSON.parse(this.mockedResponse);
  82. } catch (err) {
  83. appEvents.emit('alert-error', ['Failed to parse mocked response']);
  84. return;
  85. }
  86. data.data = mockedData;
  87. }
  88. onRequestResponse(data) {
  89. // ignore if closed
  90. if (!this.isOpen) {
  91. return;
  92. }
  93. if (this.isMocking) {
  94. this.handleMocking(data);
  95. return;
  96. }
  97. this.isLoading = false;
  98. data = _.cloneDeep(data);
  99. if (data.headers) {
  100. delete data.headers;
  101. }
  102. if (data.config) {
  103. data.request = data.config;
  104. delete data.config;
  105. delete data.request.transformRequest;
  106. delete data.request.transformResponse;
  107. delete data.request.paramSerializer;
  108. delete data.request.jsonpCallbackParam;
  109. delete data.request.headers;
  110. delete data.request.requestId;
  111. delete data.request.inspect;
  112. delete data.request.retry;
  113. delete data.request.timeout;
  114. }
  115. if (data.data) {
  116. data.response = data.data;
  117. if (data.status === 200) {
  118. // if we are in error state, assume we automatically opened
  119. // and auto close it again
  120. if (this.hasError) {
  121. this.hasError = false;
  122. this.isOpen = false;
  123. }
  124. }
  125. delete data.data;
  126. delete data.status;
  127. delete data.statusText;
  128. delete data.$$config;
  129. }
  130. this.$timeout(_.partial(this.renderJsonExplorer, data));
  131. }
  132. toggleExpand(depth) {
  133. if (this.jsonExplorer) {
  134. this.allNodesExpanded = !this.allNodesExpanded;
  135. this.jsonExplorer.openAtDepth(this.allNodesExpanded ? 20 : 1);
  136. }
  137. }
  138. }
  139. export function queryTroubleshooter() {
  140. return {
  141. restrict: 'E',
  142. template: template,
  143. controller: QueryTroubleshooterCtrl,
  144. bindToController: true,
  145. controllerAs: 'ctrl',
  146. scope: {
  147. panelCtrl: "=",
  148. isOpen: "=",
  149. },
  150. link: function(scope, elem, attrs, ctrl) {
  151. ctrl.renderJsonExplorer = function(data) {
  152. var jsonElem = elem.find('.query-troubleshooter-json');
  153. ctrl.jsonExplorer = new JsonExplorer(data, 3, {
  154. animateOpen: true,
  155. });
  156. const html = ctrl.jsonExplorer.render(true);
  157. jsonElem.html(html);
  158. };
  159. }
  160. };
  161. }
  162. coreModule.directive('queryTroubleshooter', queryTroubleshooter);