Quellcode durchsuchen

stackdriver: return values for services and metric types

Erik Sundell vor 7 Jahren
Ursprung
Commit
7ccce76b80

+ 2 - 6
public/app/features/templating/DefaultTemplateQueryComponent.tsx

@@ -1,11 +1,7 @@
 import React, { PureComponent } from 'react';
+import { TemplateQueryProps } from 'app/types/plugins';
 
-interface Props {
-  query: string;
-  onChange: (c: string) => void;
-}
-
-export default class DefaultTemplateQueryComponent extends PureComponent<Props, any> {
+export default class DefaultTemplateQueryComponent extends PureComponent<TemplateQueryProps, any> {
   constructor(props) {
     super(props);
     this.state = { value: props.query };

+ 1 - 0
public/app/features/templating/editor_ctrl.ts

@@ -72,6 +72,7 @@ export class VariableEditorCtrl {
 
       if (
         $scope.current.type === 'query' &&
+        _.isString($scope.current.query) &&
         $scope.current.query.match(new RegExp('\\$' + $scope.current.name + '(/| |$)'))
       ) {
         appEvents.emit('alert-warning', [

+ 36 - 0
public/app/plugins/datasource/stackdriver/StackdriverMetricFindQuery.ts

@@ -0,0 +1,36 @@
+import { extractServicesFromMetricDescriptors, getMetricTypesByService } from './functions';
+
+export default class StackdriverMetricFindQuery {
+  constructor(private datasource) {}
+
+  async query(query: any) {
+    switch (query.type) {
+      case 'services':
+        return this.handleServiceQueryType();
+      case 'metricTypes':
+        return this.handleMetricTypesQueryType(query);
+      default:
+        return [];
+    }
+  }
+
+  async handleServiceQueryType() {
+    const metricDescriptors = await this.datasource.getMetricTypes(this.datasource.projectName);
+    const services = extractServicesFromMetricDescriptors(metricDescriptors);
+    return services.map(s => ({
+      text: s.name,
+      expandable: true,
+    }));
+  }
+
+  async handleMetricTypesQueryType({ service }) {
+    if (!service) {
+      return [];
+    }
+    const metricDescriptors = await this.datasource.getMetricTypes(this.datasource.projectName);
+    return getMetricTypesByService(metricDescriptors, service).map(s => ({
+      text: s.name,
+      expandable: true,
+    }));
+  }
+}

+ 2 - 1
public/app/plugins/datasource/stackdriver/components/MetricTypeSelector.tsx

@@ -1,4 +1,5 @@
 import React, { SFC } from 'react';
+import { getMetricTypesByService } from '../functions';
 
 interface Props {
   onMetricTypeChange: any;
@@ -12,7 +13,7 @@ const MetricTypeSelector: SFC<Props> = props => {
       return [];
     }
 
-    return props.metricDescriptors.filter(m => m.service === props.selectedService).map(m => ({
+    return getMetricTypesByService(props.metricDescriptors, props.selectedService).map(m => ({
       value: m.service,
       name: m.displayName,
     }));

+ 4 - 3
public/app/plugins/datasource/stackdriver/components/ServiceSelector.tsx

@@ -1,5 +1,5 @@
 import React, { SFC } from 'react';
-import uniqBy from 'lodash/uniqBy';
+import { extractServicesFromMetricDescriptors } from '../functions';
 
 interface Props {
   onServiceChange: any;
@@ -7,11 +7,12 @@ interface Props {
 }
 
 const ServiceSelector: SFC<Props> = props => {
-  const extractServices = () =>
-    uniqBy(props.metricDescriptors, 'service').map(m => ({
+  const extractServices = () => {
+    return extractServicesFromMetricDescriptors(props.metricDescriptors).map(m => ({
       value: m.service,
       name: m.serviceShortName,
     }));
+  };
 
   return (
     <div className="gf-form max-width-21">

+ 14 - 12
public/app/plugins/datasource/stackdriver/components/TemplateQueryComponent.tsx

@@ -1,27 +1,24 @@
 import React, { PureComponent } from 'react';
-import StackdriverDatasource from '../datasource';
+// import StackdriverDatasource from '../datasource';
 import ServiceSelector from './ServiceSelector';
 import MetricTypeSelector from './MetricTypeSelector';
+import { TemplateQueryProps } from 'app/types/plugins';
+import defaultsDeep from 'lodash/defaultsDeep';
 
-interface Props {
-  datasource: StackdriverDatasource;
-  query: any;
-  onChange: (c: string) => void;
-}
-
-export class StackdriverTemplateQueryComponent extends PureComponent<Props, any> {
+export class StackdriverTemplateQueryComponent extends PureComponent<TemplateQueryProps, any> {
   queryTypes: Array<{ value: string; name: string }> = [
     { value: 'services', name: 'Services' },
     { value: 'metricTypes', name: 'Metric Types' },
     { value: 'metricLabels', name: 'Metric labels For Metric Type' },
   ];
+  defaults = { type: undefined, metricDescriptors: [], service: undefined, metricType: undefined };
 
-  constructor(props) {
+  constructor(props: TemplateQueryProps) {
     super(props);
     this.handleQueryTypeChange = this.handleQueryTypeChange.bind(this);
     this.onServiceChange = this.onServiceChange.bind(this);
     this.onMetricTypeChange = this.onMetricTypeChange.bind(this);
-    this.state = { queryType: undefined, metricDescriptors: [], service: undefined, metricType: undefined };
+    this.state = defaultsDeep(this.props.query, this.defaults);
   }
 
   async componentDidMount() {
@@ -30,7 +27,7 @@ export class StackdriverTemplateQueryComponent extends PureComponent<Props, any>
   }
 
   handleQueryTypeChange(event) {
-    this.setState({ queryType: event.target.value });
+    this.setState({ type: event.target.value });
   }
 
   onServiceChange(event) {
@@ -41,6 +38,11 @@ export class StackdriverTemplateQueryComponent extends PureComponent<Props, any>
     this.setState({ metricType: event.target.value });
   }
 
+  componentDidUpdate() {
+    const { metricDescriptors, ...queryModel } = this.state;
+    this.props.onChange(queryModel);
+  }
+
   renderSwitch(queryType) {
     switch (queryType) {
       case 'metricTypes':
@@ -78,7 +80,7 @@ export class StackdriverTemplateQueryComponent extends PureComponent<Props, any>
             </select>
           </div>
         </div>
-        {this.renderSwitch(this.state.queryType)}
+        {this.renderSwitch(this.state.type)}
       </React.Fragment>
     );
   }

+ 20 - 13
public/app/plugins/datasource/stackdriver/datasource.ts

@@ -1,6 +1,7 @@
 import { stackdriverUnitMappings } from './constants';
 import appEvents from 'app/core/app_events';
 import _ from 'lodash';
+import StackdriverMetricFindQuery from './StackdriverMetricFindQuery';
 
 export default class StackdriverDatasource {
   id: number;
@@ -9,6 +10,7 @@ export default class StackdriverDatasource {
   projectName: string;
   authenticationType: string;
   queryPromise: Promise<any>;
+  metricTypes: any[];
 
   /** @ngInject */
   constructor(instanceSettings, private backendSrv, private templateSrv, private timeSrv) {
@@ -18,6 +20,7 @@ export default class StackdriverDatasource {
     this.id = instanceSettings.id;
     this.projectName = instanceSettings.jsonData.defaultProject || '';
     this.authenticationType = instanceSettings.jsonData.authenticationType || 'jwt';
+    this.metricTypes = [];
   }
 
   async getTimeSeries(options) {
@@ -177,8 +180,10 @@ export default class StackdriverDatasource {
     return results;
   }
 
-  metricFindQuery(query) {
-    throw new Error('Template variables support is not yet imlemented');
+  async metricFindQuery(query) {
+    const stackdriverMetricFindQuery = new StackdriverMetricFindQuery(this);
+    return stackdriverMetricFindQuery.query(query);
+    // throw new Error('Template variables support is not yet imlemented');
   }
 
   async testDatasource() {
@@ -258,19 +263,21 @@ export default class StackdriverDatasource {
 
   async getMetricTypes(projectName: string) {
     try {
-      const metricsApiPath = `v3/projects/${projectName}/metricDescriptors`;
-      const { data } = await this.doRequest(`${this.baseUrl}${metricsApiPath}`);
+      if (this.metricTypes.length === 0) {
+        const metricsApiPath = `v3/projects/${projectName}/metricDescriptors`;
+        const { data } = await this.doRequest(`${this.baseUrl}${metricsApiPath}`);
 
-      const metrics = data.metricDescriptors.map(m => {
-        const [service] = m.type.split('/');
-        const [serviceShortName] = service.split('.');
-        m.service = service;
-        m.serviceShortName = serviceShortName;
-        m.displayName = m.displayName || m.type;
-        return m;
-      });
+        this.metricTypes = data.metricDescriptors.map(m => {
+          const [service] = m.type.split('/');
+          const [serviceShortName] = service.split('.');
+          m.service = service;
+          m.serviceShortName = serviceShortName;
+          m.displayName = m.displayName || m.type;
+          return m;
+        });
+      }
 
-      return metrics;
+      return this.metricTypes;
     } catch (error) {
       appEvents.emit('ds-request-error', this.formatStackdriverError(error));
       return [];

+ 6 - 0
public/app/plugins/datasource/stackdriver/functions.ts

@@ -0,0 +1,6 @@
+import uniqBy from 'lodash/uniqBy';
+
+export const extractServicesFromMetricDescriptors = metricDescriptors => uniqBy(metricDescriptors, 'service');
+
+export const getMetricTypesByService = (metricDescriptors, service) =>
+  metricDescriptors.filter(m => m.service === service);

+ 7 - 0
public/app/types/plugins.ts

@@ -99,3 +99,10 @@ export interface PluginsState {
   hasFetched: boolean;
   dashboards: PluginDashboard[];
 }
+
+export interface TemplateQueryProps {
+  query: any;
+  onChange: (c: any) => void;
+  datasource: any;
+  // datasource: StackdriverDatasource;
+}