Browse Source

Stackdriver: Loads project name and metrics descriptions into the query controller

Erik Sundell 7 years ago
parent
commit
75c002645c

+ 14 - 0
public/app/plugins/datasource/stackdriver/datasource.ts

@@ -9,6 +9,10 @@ export default class StackdriverDatasource {
     this.doRequest = this.doRequest;
   }
 
+  query() {
+    return Promise.resolve();
+  }
+
   testDatasource() {
     const path = `v3/projects/raintank-production/metricDescriptors`;
     return this.doRequest(`${this.baseUrl}${path}`)
@@ -48,6 +52,16 @@ export default class StackdriverDatasource {
     return response.data.projects.map(p => ({ id: p.projectId, name: p.name }));
   }
 
+  async getMetricTypes(projectId: string) {
+    try {
+      const metricsApiPath = `v3/projects/${projectId}/metricDescriptors`;
+      const { data } = await this.doRequest(`${this.baseUrl}${metricsApiPath}`);
+      return data.metricDescriptors.map(m => ({ id: m.name, name: m.displayName }));
+    } catch (error) {
+      console.log(error);
+    }
+  }
+
   async doRequest(url, maxRetries = 1) {
     return this.backendSrv
       .datasourceRequest({

+ 2 - 2
public/app/plugins/datasource/stackdriver/module.ts

@@ -1,5 +1,5 @@
 import StackdriverDatasource from './datasource';
-// import { StackdriverQueryCtrl } from './query_ctrl';
+import { StackdriverQueryCtrl } from './query_ctrl';
 import { StackdriverConfigCtrl } from './config_ctrl';
 
 // class AnnotationsQueryCtrl {
@@ -8,7 +8,7 @@ import { StackdriverConfigCtrl } from './config_ctrl';
 
 export {
   StackdriverDatasource as Datasource,
-  // StackdriverQueryCtrl as QueryCtrl,
+  StackdriverQueryCtrl as QueryCtrl,
   StackdriverConfigCtrl as ConfigCtrl,
   // AnnotationsQueryCtrl,
 };

+ 12 - 77
public/app/plugins/datasource/stackdriver/partials/query.editor.html

@@ -1,81 +1,16 @@
 <query-editor-row query-ctrl="ctrl" has-text-edit-mode="true">
-
-  <div class="gf-form" ng-show="ctrl.target.textEditor">
-    <input type="text" class="gf-form-input" ng-model="ctrl.target.target" spellcheck="false" ng-blur="ctrl.targetTextChanged()"></input>
-  </div>
-
-  <div ng-hide="ctrl.target.textEditor">
-    <div class="gf-form-inline">
-      <div class="gf-form">
-        <label class="gf-form-label width-6 query-keyword">Series</label>
-      </div>
-
-      <div ng-if="ctrl.queryModel.seriesByTagUsed" ng-repeat="tag in ctrl.queryModel.tags" class="gf-form">
-        <gf-form-dropdown
-          model="tag.key"
-          allow-custom="true"
-          label-mode="true"
-          debounce="true"
-          placeholder="Tag key"
-          css-class="query-segment-key"
-          get-options="ctrl.getTags($index, $query)"
-          on-change="ctrl.tagChanged(tag, $index)"
-        />
-        <gf-form-dropdown
-          model="tag.operator"
-          label-mode="true"
-          css-class="query-segment-operator"
-          get-options="ctrl.getTagOperators()"
-          on-change="ctrl.tagChanged(tag, $index)"
-          min-input-width="30"
-        />
-        <gf-form-dropdown
-          model="tag.value"
-          allow-custom="true"
-          label-mode="true"
-          debounce="true"
-          css-class="query-segment-value"
-          placeholder="Tag value"
-          get-options="ctrl.getTagValues(tag, $index, $query)"
-          on-change="ctrl.tagChanged(tag, $index)"
-        />
-        <label class="gf-form-label query-keyword" ng-if="ctrl.showDelimiter($index)">AND</label>
-      </div>
-
-      <div ng-if="ctrl.queryModel.seriesByTagUsed" ng-repeat="segment in ctrl.addTagSegments" role="menuitem" class="gf-form">
-        <metric-segment segment="segment" get-options="ctrl.getTagsAsSegments($query)" on-change="ctrl.addNewTag(segment)" debounce="true" />
-      </div>
-
-      <div ng-if="!ctrl.queryModel.seriesByTagUsed" ng-repeat="segment in ctrl.segments" role="menuitem" class="gf-form">
-        <metric-segment segment="segment" get-options="ctrl.getAltSegments($index, $query)" on-change="ctrl.segmentValueChanged(segment, $index)" />
-      </div>
-
-      <div ng-if="ctrl.paused" class="gf-form">
-        <a ng-click="ctrl.unpause()" class="gf-form-label query-part"><i class="fa fa-play"></i></a>
-      </div>
-
-      <div class="gf-form gf-form--grow">
-        <div class="gf-form-label gf-form-label--grow"></div>
-      </div>
+  <div class="gf-form-inline">
+    <div class="gf-form">
+      <span class="gf-form-label width-9">Project</span>
+      <input class="gf-form-input" disabled type="text" ng-model='ctrl.project.name' get-options="ctrl.getProjects()" css-class="min-width-12"
+      />
     </div>
-
-    <div class="gf-form-inline">
-      <div class="gf-form">
-        <label class="gf-form-label width-6 query-keyword">Functions</label>
-      </div>
-
-      <div ng-repeat="func in ctrl.queryModel.functions" class="gf-form">
-        <span graphite-func-editor class="gf-form-label query-part" ng-hide="func.hidden"></span>
-      </div>
-
-      <div class="gf-form dropdown">
-        <span graphite-add-func></span>
-      </div>
-
-      <div class="gf-form gf-form--grow">
-        <div class="gf-form-label gf-form-label--grow"></div>
-      </div>
+  </div>
+  <div class="gf-form-inline">
+    <div class="gf-form">
+      <span class="gf-form-label width-9">Metric Type</span>
+      <gf-form-dropdown model="ctrl.metricType" get-options="ctrl.getMetricTypes($query)" class="gf-form-input" disabled type="text"
+        allow-custom="true" lookup-text="true" css-class="min-width-12"></gf-form-dropdown>
     </div>
   </div>
-
-</query-editor-row>
+</query-editor-row>

+ 56 - 2
public/app/plugins/datasource/stackdriver/query_ctrl.ts

@@ -1,11 +1,65 @@
-import './add_graphite_func';
-import './func_editor';
+import _ from 'lodash';
 import { QueryCtrl } from 'app/plugins/sdk';
+import appEvents from 'app/core/app_events';
 
 export class StackdriverQueryCtrl extends QueryCtrl {
   static templateUrl = 'partials/query.editor.html';
+  project: {
+    id: string;
+    name: string;
+  };
+  metricType: string;
+  defaultDropdownValue = 'select';
+
   /** @ngInject */
   constructor($scope, $injector) {
     super($scope, $injector);
+    this.project = {
+      id: 'default',
+      name: 'loading project...',
+    };
+    this.metricType = this.defaultDropdownValue;
+
+    this.getCurrentProject().then(this.getMetricTypes.bind(this));
+  }
+
+  async getCurrentProject() {
+    try {
+      const projects = await this.datasource.getProjects();
+      if (projects && projects.length > 0) {
+        this.project = this.project = projects[0];
+      } else {
+        throw new Error('No projects found');
+      }
+    } catch (error) {
+      let message = 'Projects cannot be fetched: ';
+      message += error.statusText ? error.statusText + ': ' : '';
+      if (error && error.data && error.data.error && error.data.error.message) {
+        if (error.data.error.code === 403) {
+          message += `
+            A list of projects could not be fetched from the Google Cloud Resource Manager API.
+            You might need to enable it first:
+            https://console.developers.google.com/apis/library/cloudresourcemanager.googleapis.com`;
+        } else {
+          message += error.data.error.code + '. ' + error.data.error.message;
+        }
+      } else {
+        message += 'Cannot connect to Stackdriver API';
+      }
+      appEvents.emit('ds-request-error', message);
+    }
+  }
+
+  async getMetricTypes() {
+    //projects/raintank-production/metricDescriptors/agent.googleapis.com/agent/api_request_count
+    if (this.project.id !== 'default') {
+      const metricTypes = await this.datasource.getMetricTypes(this.project.id);
+      if (this.metricType === this.defaultDropdownValue && metricTypes.length > 0) {
+        this.$scope.$apply(() => (this.metricType = metricTypes[0].name));
+      }
+      return metricTypes.map(mt => ({ value: mt.id, text: mt.id }));
+    } else {
+      return [];
+    }
   }
 }