Browse Source

wip: convert angular directives to react components

Erik Sundell 7 years ago
parent
commit
146aa7abab

+ 48 - 0
public/app/plugins/datasource/stackdriver/angular_wrappers.ts

@@ -0,0 +1,48 @@
+import { react2AngularDirective } from 'app/core/utils/react2angular';
+import { QueryEditor } from './components/QueryEditor';
+// import { MetricPicker } from './components/MetricPicker';
+// import { OptionPicker } from './components/OptionPicker';
+// import { OptionGroupPicker } from './components/OptionGroupPicker';
+// import { AggregationPicker } from './components/AggregationPicker';
+
+export function registerAngularDirectives() {
+  //   react2AngularDirective('optionPicker', OptionPicker, [
+  //     'options',
+  //     'onChange',
+  //     'selected',
+  //     'searchable',
+  //     'className',
+  //     'placeholder',
+  //   ]);
+  //   react2AngularDirective('optionGroupPicker', OptionGroupPicker, [
+  //     'groups',
+  //     'onChange',
+  //     'selected',
+  //     'searchable',
+  //     'className',
+  //     'placeholder',
+  //   ]);
+  //   react2AngularDirective('metricPicker', MetricPicker, [
+  //     'target',
+  //     ['onChange', { watchDepth: 'reference' }],
+  //     'defaultProject',
+  //     'metricType',
+  //     ['templateSrv', { watchDepth: 'reference' }],
+  //     ['datasource', { watchDepth: 'reference' }],
+  //   ]);
+  //   react2AngularDirective('aggregationPicker', AggregationPicker, [
+  //     'valueType',
+  //     'metricKind',
+  //     'onChange',
+  //     'aggregation',
+  //     ['templateSrv', { watchDepth: 'reference' }],
+  //   ]);
+
+  react2AngularDirective('queryEditor', QueryEditor, [
+    'target',
+    'onChange',
+    ['uiSegmentSrv', { watchDepth: 'reference' }],
+    ['datasource', { watchDepth: 'reference' }],
+    ['templateSrv', { watchDepth: 'reference' }],
+  ]);
+}

+ 134 - 0
public/app/plugins/datasource/stackdriver/components/AggregationPicker.tsx

@@ -0,0 +1,134 @@
+import React from 'react';
+import _ from 'lodash';
+
+// import { OptionPicker } from './OptionPicker';
+import { OptionGroupPicker } from './OptionGroupPicker';
+// import { alignmentPeriods } from '../constants';
+// import { getAlignmentOptionsByMetric, getAggregationOptionsByMetric } from '../functions';
+import { getAggregationOptionsByMetric } from '../functions';
+// import kbn from 'app/core/utils/kbn';
+
+export interface Props {
+  onChange: (metricDescriptor) => void;
+  templateSrv: any;
+  valueType: string;
+  metricKind: string;
+  aggregation: {
+    crossSeriesReducer: string;
+    alignmentPeriod: string;
+    perSeriesAligner: string;
+    groupBys: string[];
+  };
+}
+
+interface State {
+  alignmentPeriods: any[];
+  alignOptions: any[];
+  aggOptions: any[];
+}
+
+export class AggregationPicker extends React.Component<Props, State> {
+  state: State = {
+    alignmentPeriods: [],
+    alignOptions: [],
+    aggOptions: [],
+  };
+
+  constructor(props) {
+    super(props);
+  }
+
+  componentDidMount() {
+    this.setAggOptions();
+  }
+
+  componentWillReceiveProps(nextProps: Props) {
+    const { valueType, metricKind, aggregation } = this.props;
+    if (
+      nextProps.valueType !== valueType ||
+      nextProps.metricKind !== metricKind ||
+      nextProps.aggregation.groupBys !== aggregation.groupBys
+    ) {
+      this.setAggOptions();
+    }
+  }
+
+  setAggOptions() {
+    const { valueType, metricKind, aggregation, templateSrv } = this.props;
+    let aggregations = getAggregationOptionsByMetric(valueType, metricKind).map(a => ({
+      ...a,
+      label: a.text,
+    }));
+    if (!aggregations.find(o => o.value === templateSrv.replace(aggregation.crossSeriesReducer))) {
+      this.deselectAggregationOption('REDUCE_NONE');
+    }
+
+    if (aggregation.groupBys.length > 0) {
+      aggregations = aggregations.filter(o => o.value !== 'REDUCE_NONE');
+      this.deselectAggregationOption('REDUCE_NONE');
+    }
+    this.setState({
+      aggOptions: [
+        this.getTemplateVariablesGroup(),
+        {
+          label: 'Aggregations',
+          options: aggregations,
+        },
+      ],
+    });
+  }
+
+  deselectAggregationOption(notValidOptionValue: string) {
+    const aggregations = getAggregationOptionsByMetric(this.props.valueType, this.props.metricKind);
+    const newValue = aggregations.find(o => o.value !== notValidOptionValue);
+    this.handleAggregationChange(newValue ? newValue.value : '');
+  }
+
+  handleAggregationChange(value) {
+    this.props.onChange(value);
+    // this.$scope.refresh();
+  }
+
+  getTemplateVariablesGroup() {
+    return {
+      label: 'Template Variables',
+      options: this.props.templateSrv.variables.map(v => ({
+        label: `$${v.name}`,
+        value: `$${v.name}`,
+      })),
+    };
+  }
+
+  render() {
+    const { aggOptions } = this.state;
+    const { aggregation } = this.props;
+
+    return (
+      <React.Fragment>
+        <div className="gf-form-inline">
+          <div className="gf-form">
+            <label className="gf-form-label query-keyword width-9">Aggregation</label>
+            <div className="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
+              <OptionGroupPicker
+                onChange={value => this.handleAggregationChange(value)}
+                selected={aggregation.crossSeriesReducer}
+                groups={aggOptions}
+                searchable={true}
+                placeholder="Select Aggregation"
+                className="width-15"
+              />
+            </div>
+          </div>
+          <div className="gf-form gf-form--grow">
+            <label className="gf-form-label gf-form-label--grow">
+              <a ng-click="ctrl.target.showAggregationOptions = !ctrl.target.showAggregationOptions">
+                <i className="fa fa-caret-down" ng-show="ctrl.target.showAggregationOptions" />
+                <i className="fa fa-caret-right" ng-hide="ctrl.target.showAggregationOptions" /> Advanced Options
+              </a>
+            </label>
+          </div>
+        </div>
+      </React.Fragment>
+    );
+  }
+}

+ 239 - 0
public/app/plugins/datasource/stackdriver/components/Filter.tsx

@@ -0,0 +1,239 @@
+import React from 'react';
+import _ from 'lodash';
+
+import Segment from './Segment';
+import { QueryMeta, Target } from '../types';
+import { FilterSegments } from '../filter_segments';
+
+export interface Props {
+  onChange: (metricDescriptor) => void;
+  templateSrv: any;
+  labelData: QueryMeta;
+  loading: Promise<any>;
+  target: Target;
+  uiSegmentSrv: any;
+}
+
+interface State {
+  defaultRemoveGroupByValue: string;
+  resourceTypeValue: string;
+  groupBySegments: any[];
+  // filterSegments: FilterSegments;
+  filterSegments: any;
+  removeSegment?: any;
+}
+
+export class Filter extends React.Component<Props, State> {
+  state: State = {
+    defaultRemoveGroupByValue: '-- remove group by --',
+    resourceTypeValue: 'resource.type',
+    groupBySegments: [],
+    filterSegments: new FilterSegments(this.getFilterKeys.bind(this), this.getFilterValues.bind(this)),
+  };
+
+  constructor(props) {
+    super(props);
+  }
+
+  componentDidMount() {
+    this.initSegments(false);
+  }
+
+  shouldComponentUpdate(nextProps) {
+    return this.state.filterSegments.filterSegments.length > 0;
+  }
+
+  initSegments(hideGroupBys: boolean) {
+    this.state.filterSegments.init(this.props.uiSegmentSrv);
+    if (!hideGroupBys) {
+      this.setState({
+        groupBySegments: this.props.target.aggregation.groupBys.map(groupBy => {
+          return this.props.uiSegmentSrv.getSegmentForValue(groupBy);
+        }),
+      });
+
+      this.ensurePlusButton(this.state.groupBySegments);
+    }
+
+    this.setState({
+      removeSegment: this.props.uiSegmentSrv.newSegment({ fake: true, value: '-- remove group by --' }),
+    });
+
+    this.state.filterSegments.buildSegmentModel(this.props.target.filters);
+  }
+
+  async createLabelKeyElements() {
+    await this.props.loading;
+
+    let elements = Object.keys(this.props.labelData.metricLabels || {}).map(l => {
+      return this.props.uiSegmentSrv.newSegment({
+        value: `metric.label.${l}`,
+        expandable: false,
+      });
+    });
+
+    elements = [
+      ...elements,
+      ...Object.keys(this.props.labelData.resourceLabels || {}).map(l => {
+        return this.props.uiSegmentSrv.newSegment({
+          value: `resource.label.${l}`,
+          expandable: false,
+        });
+      }),
+    ];
+
+    if (this.props.labelData.resourceTypes && this.props.labelData.resourceTypes.length > 0) {
+      elements = [
+        ...elements,
+        this.props.uiSegmentSrv.newSegment({
+          value: this.state.resourceTypeValue,
+          expandable: false,
+        }),
+      ];
+    }
+
+    return elements;
+  }
+
+  async getFilterKeys(segment, removeText?: string) {
+    let elements = await this.createLabelKeyElements();
+
+    if (this.props.target.filters.indexOf(this.state.resourceTypeValue) !== -1) {
+      elements = elements.filter(e => e.value !== this.state.resourceTypeValue);
+    }
+
+    const noValueOrPlusButton = !segment || segment.type === 'plus-button';
+    if (noValueOrPlusButton && elements.length === 0) {
+      return [];
+    }
+
+    return [
+      ...elements,
+      this.props.uiSegmentSrv.newSegment({ fake: true, value: removeText || this.state.defaultRemoveGroupByValue }),
+    ];
+  }
+
+  async getGroupBys(segment) {
+    let elements = await this.createLabelKeyElements();
+
+    elements = elements.filter(e => this.props.target.aggregation.groupBys.indexOf(e.value) === -1);
+    const noValueOrPlusButton = !segment || segment.type === 'plus-button';
+    if (noValueOrPlusButton && elements.length === 0) {
+      return [];
+    }
+
+    this.state.removeSegment.value = this.state.defaultRemoveGroupByValue;
+    return [...elements, this.state.removeSegment];
+  }
+
+  groupByChanged(segment, index) {
+    if (segment.value === this.state.removeSegment.value) {
+      // this.groupBySegments.splice(index, 1);
+    } else {
+      segment.type = 'value';
+    }
+
+    const reducer = (memo, seg) => {
+      if (!seg.fake) {
+        memo.push(seg.value);
+      }
+      return memo;
+    };
+
+    this.props.target.aggregation.groupBys = this.state.groupBySegments.reduce(reducer, []);
+    this.ensurePlusButton(this.state.groupBySegments);
+    // this.$rootScope.$broadcast('metricTypeChanged');
+    // this.$scope.refresh();
+  }
+
+  async getFilters(segment, index) {
+    await this.props.loading;
+    const hasNoFilterKeys =
+      this.props.labelData.metricLabels && Object.keys(this.props.labelData.metricLabels).length === 0;
+    return this.state.filterSegments.getFilters(segment, index, hasNoFilterKeys);
+  }
+
+  getFilterValues(index) {
+    const filterKey = this.props.templateSrv.replace(this.state.filterSegments.filterSegments[index - 2].value);
+    if (
+      !filterKey ||
+      !this.props.labelData.metricLabels ||
+      Object.keys(this.props.labelData.metricLabels).length === 0
+    ) {
+      return [];
+    }
+
+    const shortKey = filterKey.substring(filterKey.indexOf('.label.') + 7);
+
+    if (filterKey.startsWith('metric.label.') && this.props.labelData.metricLabels.hasOwnProperty(shortKey)) {
+      return this.props.labelData.metricLabels[shortKey];
+    }
+
+    if (filterKey.startsWith('resource.label.') && this.props.labelData.resourceLabels.hasOwnProperty(shortKey)) {
+      return this.props.labelData.resourceLabels[shortKey];
+    }
+
+    if (filterKey === this.state.resourceTypeValue) {
+      return this.props.labelData.resourceTypes;
+    }
+
+    return [];
+  }
+
+  filterSegmentUpdated(segment, index) {
+    this.props.target.filters = this.state.filterSegments.filterSegmentUpdated(segment, index);
+    // this.$scope.refresh();
+  }
+
+  ensurePlusButton(segments) {
+    const count = segments.length;
+    const lastSegment = segments[Math.max(count - 1, 0)];
+
+    if (!lastSegment || lastSegment.type !== 'plus-button') {
+      segments.push(this.props.uiSegmentSrv.newPlusButton());
+    }
+  }
+
+  render() {
+    const { filterSegments } = this.state;
+    // const { metrifilterSegmentscType } = this.props;
+
+    return (
+      <React.Fragment>
+        <div className="gf-form-inline">
+          <div className="gf-form">
+            <span className="gf-form-label query-keyword width-9">Filter</span>
+            <div className="gf-form">
+              {filterSegments.filterSegments.map((segment, i) => (
+                <Segment
+                  key={i}
+                  segment={segment}
+                  getOptions={() => this.getFilters(segment, i)}
+                  onChange={segment => this.filterSegmentUpdated(segment, i)}
+                />
+              ))}
+            </div>
+          </div>
+          <div className="gf-form gf-form--grow">
+            <div className="gf-form-label gf-form-label--grow" />
+          </div>
+        </div>
+        {/* <div className="gf-form-inline" ng-hide="ctrl.$scope.hideGroupBys">
+          <div className="gf-form">
+            <span className="gf-form-label query-keyword width-9">Group By</span>
+            <div className="gf-form" ng-repeat="segment in ctrl.groupBySegments">
+              <Segment
+                segment="segment"
+                get-options="ctrl.getGroupBys(segment)"
+                on-change="ctrl.groupByChanged(segment, $index)"
+              />
+            </div>
+          </div>
+          <div className="gf-form gf-form--grow">
+            <div className="gf-form-label gf-form-label--grow" />
+          </div>
+        </div> */}
+      </React.Fragment>
+    );
+  }
+}

+ 141 - 0
public/app/plugins/datasource/stackdriver/components/QueryEditor.tsx

@@ -0,0 +1,141 @@
+import React from 'react';
+import _ from 'lodash';
+import appEvents from 'app/core/app_events';
+
+import { MetricPicker } from './MetricPicker';
+import { Filter } from './Filter';
+// import { AggregationPicker } from './AggregationPicker';
+import { Target, QueryMeta } from '../types';
+
+export interface Props {
+  onChange: (target: Target) => void;
+  target: Target;
+  datasource: any;
+  templateSrv: any;
+  uiSegmentSrv: any;
+}
+
+interface State {
+  target: Target;
+  labelData: QueryMeta;
+  loadLabelsPromise: Promise<any>;
+}
+
+const DefaultTarget: Target = {
+  defaultProject: 'loading project...',
+  metricType: '',
+  refId: '',
+  service: '',
+  unit: '',
+  aggregation: {
+    crossSeriesReducer: 'REDUCE_MEAN',
+    alignmentPeriod: 'stackdriver-auto',
+    perSeriesAligner: 'ALIGN_MEAN',
+    groupBys: [],
+  },
+  filters: [],
+  aliasBy: '',
+  metricKind: '',
+  valueType: '',
+};
+
+export class QueryEditor extends React.Component<Props, State> {
+  state: State = { labelData: null, loadLabelsPromise: null, target: DefaultTarget };
+
+  constructor(props) {
+    super(props);
+    this.handleMetricTypeChange = this.handleMetricTypeChange.bind(this);
+    this.handleAggregationChange = this.handleAggregationChange.bind(this);
+  }
+
+  componentDidMount() {
+    this.setState({ target: this.props.target });
+    this.getLabels();
+  }
+
+  async getLabels() {
+    const loadLabelsPromise = new Promise(async resolve => {
+      try {
+        const { meta } = await this.props.datasource.getLabels(this.props.target.metricType, this.props.target.refId);
+        this.setState({ labelData: meta });
+        resolve();
+      } catch (error) {
+        appEvents.emit('alert-error', ['Error', 'Error loading metric labels for ' + this.props.target.metricType]);
+        resolve();
+      }
+    });
+    this.setState({ loadLabelsPromise });
+  }
+
+  handleMetricTypeChange({ valueType, metricKind, type, unit }) {
+    this.setState({
+      target: {
+        ...this.state.target,
+        ...{
+          metricType: type,
+          unit,
+          valueType,
+          metricKind,
+        },
+      },
+    });
+
+    // this.$rootScope.$broadcast('metricTypeChanged');
+    // this.getLabels();
+    // this.refresh();
+  }
+
+  handleAggregationChange(crossSeriesReducer) {
+    // this.target.aggregation.crossSeriesReducer = crossSeriesReducer;
+    // this.refresh();
+  }
+
+  render() {
+    const { labelData, loadLabelsPromise, target } = this.state;
+    const { defaultProject, metricType } = target;
+    const { templateSrv, datasource, uiSegmentSrv } = this.props;
+
+    return (
+      <React.Fragment>
+        <MetricPicker
+          defaultProject={defaultProject}
+          metricType={metricType}
+          templateSrv={templateSrv}
+          datasource={datasource}
+          onChange={value => this.handleMetricTypeChange(value)}
+        />
+        <Filter
+          onChange={() => console.log('change filter')}
+          target={target}
+          uiSegmentSrv={uiSegmentSrv}
+          labelData={labelData}
+          templateSrv={templateSrv}
+          loading={loadLabelsPromise}
+        />
+        {/* target="ctrl.target" refresh="ctrl.refresh()" loading="ctrl.loadLabelsPromise" label-data="ctrl.labelData" */}
+        {/* <stackdriver-filter
+          target="target"
+          refresh="target.refresh()"
+          loading="target.loadLabelsPromise"
+          label-data="target.labelData"
+        />
+        <aggregation-picker
+          value-type="target.target.valueType"
+          metric-kind="target.target.metricKind"
+          aggregation="target.target.aggregation"
+          alignment-period="target.lastQueryMeta.alignmentPeriod"
+          refresh="target.refresh()"
+          template-srv="target.templateSrv"
+          datasource="target.datasource"
+          on-change="target.handleAggregationChange"
+        />
+
+        <stackdriver-aggregation
+          target="target.target"
+          alignment-period="target.lastQueryMeta.alignmentPeriod"
+          refresh="target.refresh()"
+        /> */}
+      </React.Fragment>
+    );
+  }
+}

+ 44 - 0
public/app/plugins/datasource/stackdriver/components/Segment.tsx

@@ -0,0 +1,44 @@
+import React, { PureComponent } from 'react';
+import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
+import 'app/core/directives/metric_segment';
+
+interface QueryEditorProps {
+  segment: any;
+  getOptions: () => Promise<any[]>;
+  onChange: (segment, index) => void;
+  key: number;
+}
+
+export default class Segment extends PureComponent<QueryEditorProps, any> {
+  element: any;
+  component: AngularComponent;
+
+  async componentDidMount() {
+    if (!this.element) {
+      return;
+    }
+
+    const { segment, getOptions, onChange } = this.props;
+    const loader = getAngularLoader();
+    const template = '<metric-segment> </metric-segment>';
+
+    const scopeProps = {
+      segment,
+      onChange,
+      getOptions,
+      debounce: false,
+    };
+
+    this.component = loader.load(this.element, scopeProps, template);
+  }
+
+  componentWillUnmount() {
+    if (this.component) {
+      this.component.destroy();
+    }
+  }
+
+  render() {
+    return <div ref={element => (this.element = element)} style={{ width: '100%' }} />;
+  }
+}

+ 10 - 3
public/app/plugins/datasource/stackdriver/filter_segments.ts

@@ -4,14 +4,21 @@ export const DefaultFilterValue = 'select value';
 export class FilterSegments {
   filterSegments: any[];
   removeSegment: any;
+  uiSegmentSrv: any;
 
-  constructor(private uiSegmentSrv, private target, private getFilterKeysFunc, private getFilterValuesFunc) {}
+  constructor(private getFilterKeysFunc, private getFilterValuesFunc) {
+    this.filterSegments = [];
+  }
+
+  init(uiSegmentSrv) {
+    this.uiSegmentSrv = uiSegmentSrv;
+  }
 
-  buildSegmentModel() {
+  buildSegmentModel(filters) {
     this.removeSegment = this.uiSegmentSrv.newSegment({ fake: true, value: DefaultRemoveFilterValue });
 
     this.filterSegments = [];
-    this.target.filters.forEach((f, index) => {
+    filters.forEach((f, index) => {
       switch (index % 4) {
         case 0:
           this.filterSegments.push(this.uiSegmentSrv.newKey(f));

+ 5 - 2
public/app/plugins/datasource/stackdriver/partials/query.editor.html

@@ -1,8 +1,11 @@
 <query-editor-row query-ctrl="ctrl" has-text-edit-mode="false">
-  <metric-picker target="ctrl.target" default-project="ctrl.target.defaultProject"  metric-type="ctrl.target.metricType" template-srv="ctrl.templateSrv" datasource="ctrl.datasource" on-change="ctrl.handleMetricTypeChange"></metric-picker>
+    <query-editor target="ctrl.target" template-srv="ctrl.templateSrv" datasource="ctrl.datasource" on-change="ctrl.handleTargetChange" ui-segment-srv=ctrl.uiSegmentSrv></query-editor>
+  <!-- <metric-picker target="ctrl.target" default-project="ctrl.target.defaultProject"  metric-type="ctrl.target.metricType" template-srv="ctrl.templateSrv" datasource="ctrl.datasource" on-change="ctrl.handleMetricTypeChange"></metric-picker>
   <stackdriver-filter target="ctrl.target" refresh="ctrl.refresh()" loading="ctrl.loadLabelsPromise" label-data="ctrl.labelData"
     ></stackdriver-filter>
-  <stackdriver-aggregation target="ctrl.target" alignment-period="ctrl.lastQueryMeta.alignmentPeriod" refresh="ctrl.refresh()"></stackdriver-aggregation>
+    <aggregation-picker value-type="ctrl.target.valueType" metric-kind="ctrl.target.metricKind" aggregation="ctrl.target.aggregation" alignment-period="ctrl.lastQueryMeta.alignmentPeriod" refresh="ctrl.refresh()" template-srv="ctrl.templateSrv" datasource="ctrl.datasource" on-change="ctrl.handleAggregationChange"></aggregation-picker>
+
+  <stackdriver-aggregation target="ctrl.target" alignment-period="ctrl.lastQueryMeta.alignmentPeriod" refresh="ctrl.refresh()"></stackdriver-aggregation> -->
   <!-- <stackdriver-filter target="ctrl.target" refresh="ctrl.refresh()" datasource="ctrl.datasource"
     ></stackdriver-filter>
   <stackdriver-aggregation target="ctrl.target" alignment-period="ctrl.lastQueryMeta.alignmentPeriod" refresh="ctrl.refresh()"></stackdriver-aggregation> -->

+ 48 - 86
public/app/plugins/datasource/stackdriver/query_ctrl.ts

@@ -3,57 +3,33 @@ import appEvents from 'app/core/app_events';
 import { QueryCtrl } from 'app/plugins/sdk';
 import './query_aggregation_ctrl';
 import './query_filter_ctrl';
-import { MetricPicker } from './components/MetricPicker';
-import { OptionPicker } from './components/OptionPicker';
-import { OptionGroupPicker } from './components/OptionGroupPicker';
-import { react2AngularDirective } from 'app/core/utils/react2angular';
+import { registerAngularDirectives } from './angular_wrappers';
+import { Target, QueryMeta } from './types';
 
-export interface QueryMeta {
-  alignmentPeriod: string;
-  rawQuery: string;
-  rawQueryString: string;
-  metricLabels: { [key: string]: string[] };
-  resourceLabels: { [key: string]: string[] };
-}
+export const DefaultTarget = {
+  defaultProject: 'loading project...',
+  metricType: '',
+  service: '',
+  metric: '',
+  unit: '',
+  aggregation: {
+    crossSeriesReducer: 'REDUCE_MEAN',
+    alignmentPeriod: 'stackdriver-auto',
+    perSeriesAligner: 'ALIGN_MEAN',
+    groupBys: [],
+  },
+  filters: [],
+  showAggregationOptions: false,
+  aliasBy: '',
+  metricKind: '',
+  valueType: '',
+};
 
 export class StackdriverQueryCtrl extends QueryCtrl {
   static templateUrl = 'partials/query.editor.html';
-  target: {
-    defaultProject: string;
-    unit: string;
-    metricType: string;
-    service: string;
-    refId: string;
-    aggregation: {
-      crossSeriesReducer: string;
-      alignmentPeriod: string;
-      perSeriesAligner: string;
-      groupBys: string[];
-    };
-    filters: string[];
-    aliasBy: string;
-    metricKind: any;
-    valueType: any;
-  };
+  target: Target;
 
-  defaults = {
-    defaultProject: 'loading project...',
-    metricType: '',
-    service: '',
-    metric: '',
-    unit: '',
-    aggregation: {
-      crossSeriesReducer: 'REDUCE_MEAN',
-      alignmentPeriod: 'stackdriver-auto',
-      perSeriesAligner: 'ALIGN_MEAN',
-      groupBys: [],
-    },
-    filters: [],
-    showAggregationOptions: false,
-    aliasBy: '',
-    metricKind: '',
-    valueType: '',
-  };
+  defaults = DefaultTarget;
 
   showHelp: boolean;
   showLastQuery: boolean;
@@ -63,50 +39,41 @@ export class StackdriverQueryCtrl extends QueryCtrl {
 
   loadLabelsPromise: Promise<any>;
   templateSrv: any;
+  $rootScope: any;
+  uiSegmentSrv: any;
 
   /** @ngInject */
-  constructor($scope, $injector, templateSrv, private $rootScope) {
+  constructor($scope, $injector, templateSrv, $rootScope, uiSegmentSrv) {
     super($scope, $injector);
     this.templateSrv = templateSrv;
+    this.$rootScope = $rootScope;
+    this.uiSegmentSrv = uiSegmentSrv;
     _.defaultsDeep(this.target, this.defaults);
     this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope);
     this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope);
-    this.handleMetricTypeChange = this.handleMetricTypeChange.bind(this);
-    react2AngularDirective('optionPicker', OptionPicker, [
-      'options',
-      'onChange',
-      'selected',
-      'searchable',
-      'className',
-      'placeholder',
-    ]);
-    react2AngularDirective('optionGroupPicker', OptionGroupPicker, [
-      'groups',
-      'onChange',
-      'selected',
-      'searchable',
-      'className',
-      'placeholder',
-    ]);
-    react2AngularDirective('metricPicker', MetricPicker, [
-      'target',
-      ['onChange', { watchDepth: 'reference' }],
-      'defaultProject',
-      'metricType',
-      ['templateSrv', { watchDepth: 'reference' }],
-      ['datasource', { watchDepth: 'reference' }],
-    ]);
-    this.getLabels();
+    // this.handleMetricTypeChange = this.handleMetricTypeChange.bind(this);
+    // this.handleAggregationChange = this.handleAggregationChange.bind(this);
+    this.handleTargetChange = this.handleTargetChange.bind(this);
+    registerAngularDirectives();
+    // this.getLabels();
   }
 
-  handleMetricTypeChange({ valueType, metricKind, type, unit }) {
-    this.target.metricType = type;
-    this.target.unit = unit;
-    this.target.valueType = valueType;
-    this.target.metricKind = metricKind;
-    this.$rootScope.$broadcast('metricTypeChanged');
-    this.getLabels();
-    this.refresh();
+  // handleMetricTypeChange({ valueType, metricKind, type, unit }) {
+  //   this.target.metricType = type;
+  //   this.target.unit = unit;
+  //   this.target.valueType = valueType;
+  //   this.target.metricKind = metricKind;
+  //   this.$rootScope.$broadcast('metricTypeChanged');
+  //   this.getLabels();
+  //   this.refresh();
+  // }
+
+  // handleAggregationChange(crossSeriesReducer) {
+  //   this.target.aggregation.crossSeriesReducer = crossSeriesReducer;
+  //   this.refresh();
+  // }
+  handleTargetChange(target: Target) {
+    console.log(target);
   }
 
   async getLabels() {
@@ -116,11 +83,6 @@ export class StackdriverQueryCtrl extends QueryCtrl {
         this.labelData = meta;
         resolve();
       } catch (error) {
-        if (error.data && error.data.message) {
-          console.log(error.data.message);
-        } else {
-          console.log(error);
-        }
         appEvents.emit('alert-error', ['Error', 'Error loading metric labels for ' + this.target.metricType]);
         resolve();
       }

+ 8 - 8
public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts

@@ -1,7 +1,7 @@
 import coreModule from 'app/core/core_module';
 import _ from 'lodash';
 import { FilterSegments } from './filter_segments';
-import { QueryMeta } from './query_ctrl';
+import { QueryMeta } from './types';
 // import appEvents from 'app/core/app_events';
 
 export class StackdriverFilter {
@@ -62,13 +62,13 @@ export class StackdriverFilterCtrl {
 
     this.removeSegment = this.uiSegmentSrv.newSegment({ fake: true, value: '-- remove group by --' });
 
-    this.filterSegments = new FilterSegments(
-      this.uiSegmentSrv,
-      this.target,
-      this.getFilterKeys.bind(this),
-      this.getFilterValues.bind(this)
-    );
-    this.filterSegments.buildSegmentModel();
+    // this.filterSegments = new FilterSegments(
+    //   this.uiSegmentSrv,
+    //   this.target,
+    //   this.getFilterKeys.bind(this),
+    //   this.getFilterValues.bind(this)
+    // );
+    // this.filterSegments.buildSegmentModel();
   }
 
   // async getLabels() {

+ 27 - 0
public/app/plugins/datasource/stackdriver/types.ts

@@ -19,3 +19,30 @@ export interface VariableQueryData {
   metricTypes: Array<{ value: string; name: string }>;
   services: Array<{ value: string; name: string }>;
 }
+
+export interface Target {
+  defaultProject: string;
+  unit: string;
+  metricType: string;
+  service: string;
+  refId: string;
+  aggregation: {
+    crossSeriesReducer: string;
+    alignmentPeriod: string;
+    perSeriesAligner: string;
+    groupBys: string[];
+  };
+  filters: string[];
+  aliasBy: string;
+  metricKind: any;
+  valueType: any;
+}
+
+export interface QueryMeta {
+  alignmentPeriod: string;
+  rawQuery: string;
+  rawQueryString: string;
+  metricLabels: { [key: string]: string[] };
+  resourceLabels: { [key: string]: string[] };
+  resourceTypes: string[];
+}