Erik Sundell 7 rokov pred
rodič
commit
a955caa9ac

+ 2 - 1
public/app/plugins/datasource/stackdriver/angular_wrappers.ts

@@ -4,7 +4,8 @@ import { QueryEditor } from './components/QueryEditor';
 export function registerAngularDirectives() {
   react2AngularDirective('queryEditor', QueryEditor, [
     'target',
-    'onChange',
+    'onQueryChange',
+    'onExecuteQuery',
     ['uiSegmentSrv', { watchDepth: 'reference' }],
     ['datasource', { watchDepth: 'reference' }],
     ['templateSrv', { watchDepth: 'reference' }],

+ 19 - 35
public/app/plugins/datasource/stackdriver/components/AggregationPicker.tsx

@@ -39,7 +39,7 @@ export class AggregationPicker extends React.Component<Props, State> {
   }
 
   componentDidMount() {
-    this.setAggOptions();
+    this.setAggOptions(this.props);
   }
 
   componentWillReceiveProps(nextProps: Props) {
@@ -49,17 +49,21 @@ export class AggregationPicker extends React.Component<Props, State> {
       nextProps.metricKind !== metricKind ||
       nextProps.aggregation.groupBys !== aggregation.groupBys
     ) {
-      this.setAggOptions();
+      this.setAggOptions(nextProps);
     }
   }
 
-  setAggOptions() {
-    const { valueType, metricKind, aggregation, templateSrv } = this.props;
+  setAggOptions({ valueType, metricKind, aggregation }) {
+    const { templateSrv } = this.props;
     let aggregations = getAggregationOptionsByMetric(valueType, metricKind).map(a => ({
       ...a,
       label: a.text,
     }));
-    if (!aggregations.find(o => o.value === templateSrv.replace(aggregation.crossSeriesReducer))) {
+
+    if (
+      aggregations.length > 0 &&
+      !aggregations.find(o => o.value === templateSrv.replace(aggregation.crossSeriesReducer))
+    ) {
       this.deselectAggregationOption('REDUCE_NONE');
     }
 
@@ -67,15 +71,7 @@ export class AggregationPicker extends React.Component<Props, State> {
       aggregations = aggregations.filter(o => o.value !== 'REDUCE_NONE');
       this.deselectAggregationOption('REDUCE_NONE');
     }
-    this.setState({
-      aggOptions: [
-        this.getTemplateVariablesGroup(),
-        {
-          label: 'Aggregations',
-          options: aggregations,
-        },
-      ],
-    });
+    this.setState({ aggOptions: aggregations });
   }
 
   deselectAggregationOption(notValidOptionValue: string) {
@@ -86,17 +82,6 @@ export class AggregationPicker extends React.Component<Props, State> {
 
   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() {
@@ -108,16 +93,15 @@ export class AggregationPicker extends React.Component<Props, State> {
         <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">
-              <StackdriverPicker
-                onChange={value => this.handleAggregationChange(value)}
-                selected={aggregation.crossSeriesReducer}
-                options={aggOptions}
-                searchable={true}
-                placeholder="Select Aggregation"
-                className="width-15"
-              />
-            </div>
+            <StackdriverPicker
+              onChange={value => this.handleAggregationChange(value)}
+              selected={aggregation.crossSeriesReducer}
+              options={aggOptions}
+              searchable={true}
+              placeholder="Select Aggregation"
+              className="width-15"
+              groupName="Aggregations"
+            />
           </div>
           <div className="gf-form gf-form--grow">
             <label className="gf-form-label gf-form-label--grow">

+ 29 - 210
public/app/plugins/datasource/stackdriver/components/Filter.tsx

@@ -1,12 +1,13 @@
 import React from 'react';
 import _ from 'lodash';
 
-import Segment from './Segment';
 import { QueryMeta, Target } from '../types';
-import { FilterSegments } from '../filter_segments';
+import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
+import '../query_filter_ctrl';
 
 export interface Props {
-  onChange: (metricDescriptor) => void;
+  filtersChanged: (filters) => void;
+  groupBysChanged: (groupBys) => void;
   templateSrv: any;
   labelData: QueryMeta;
   loading: Promise<any>;
@@ -14,226 +15,44 @@ export interface Props {
   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 --' }),
-    });
+export class Filter extends React.Component<Props, any> {
+  element: any;
+  component: AngularComponent;
 
-    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);
+  async componentDidMount() {
+    if (!this.element) {
+      return;
     }
 
-    const noValueOrPlusButton = !segment || segment.type === 'plus-button';
-    if (noValueOrPlusButton && elements.length === 0) {
-      return [];
-    }
+    const { loading, labelData, target, filtersChanged, groupBysChanged } = this.props;
+    const loader = getAngularLoader();
+    const template = '<stackdriver-filter> </stackdriver-filter>';
 
-    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;
+    const scopeProps = {
+      loading,
+      labelData,
+      target,
+      filtersChanged,
+      groupBysChanged,
     };
 
-    this.props.target.aggregation.groupBys = this.state.groupBySegments.reduce(reducer, []);
-    this.ensurePlusButton(this.state.groupBySegments);
-    // this.$rootScope.$broadcast('metricTypeChanged');
-    // this.$scope.refresh();
+    this.component = loader.load(this.element, scopeProps, template);
   }
 
-  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);
+  componentDidUpdate() {
+    const scope = this.component.getScope();
+    scope.loading = _.clone(this.props.loading);
+    scope.labelData = _.cloneDeep(this.props.labelData);
+    scope.target = _.cloneDeep(this.props.target);
   }
 
-  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());
+  componentWillUnmount() {
+    if (this.component) {
+      this.component.destroy();
     }
   }
 
   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>
-    );
+    return <div ref={element => (this.element = element)} style={{ width: '100%' }} />;
   }
 }

+ 54 - 19
public/app/plugins/datasource/stackdriver/components/QueryEditor.tsx

@@ -4,11 +4,12 @@ import appEvents from 'app/core/app_events';
 
 import { MetricPicker } from './MetricPicker';
 import { Filter } from './Filter';
-// import { AggregationPicker } from './AggregationPicker';
+import { AggregationPicker } from './AggregationPicker';
 import { Target, QueryMeta } from '../types';
 
 export interface Props {
-  onChange: (target: Target) => void;
+  onQueryChange: (target: Target) => void;
+  onExecuteQuery?: () => void;
   target: Target;
   datasource: any;
   templateSrv: any;
@@ -24,6 +25,8 @@ interface State {
 const DefaultTarget: Target = {
   defaultProject: 'loading project...',
   metricType: '',
+  metricKind: '',
+  valueType: '',
   refId: '',
   service: '',
   unit: '',
@@ -35,22 +38,14 @@ const DefaultTarget: Target = {
   },
   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);
-  }
+  state: State = { labelData: null, loadLabelsPromise: new Promise(() => {}), target: DefaultTarget };
 
   componentDidMount() {
-    this.setState({ target: this.props.target });
     this.getLabels();
+    this.setState({ target: this.props.target });
   }
 
   async getLabels() {
@@ -81,18 +76,50 @@ export class QueryEditor extends React.Component<Props, State> {
     });
 
     // this.$rootScope.$broadcast('metricTypeChanged');
-    // this.getLabels();
-    // this.refresh();
+    this.getLabels();
+    this.props.onQueryChange(this.state.target);
+    this.props.onExecuteQuery();
   }
 
-  handleAggregationChange(crossSeriesReducer) {
-    // this.target.aggregation.crossSeriesReducer = crossSeriesReducer;
-    // this.refresh();
+  handleFilterChange(value) {
+    this.setState({
+      target: {
+        ...this.state.target,
+        filters: value,
+      },
+    });
+    this.props.onQueryChange(this.state.target);
+    this.props.onExecuteQuery();
+  }
+
+  handleGroupBysChange(value) {
+    this.setState({
+      target: {
+        ...this.state.target,
+        groupBys: value,
+      },
+    });
+    this.props.onQueryChange(this.state.target);
+    this.props.onExecuteQuery();
+  }
+
+  handleAggregationChange(value) {
+    this.setState({
+      target: {
+        ...this.state.target,
+        aggregation: {
+          ...this.state.target.aggregation,
+          crossSeriesReducer: value,
+        },
+      },
+    });
+    this.props.onQueryChange(this.state.target);
+    this.props.onExecuteQuery();
   }
 
   render() {
     const { labelData, loadLabelsPromise, target } = this.state;
-    const { defaultProject, metricType } = target;
+    const { defaultProject, metricType, valueType, metricKind, aggregation } = target;
     const { templateSrv, datasource, uiSegmentSrv } = this.props;
 
     return (
@@ -105,13 +132,21 @@ export class QueryEditor extends React.Component<Props, State> {
           onChange={value => this.handleMetricTypeChange(value)}
         />
         <Filter
-          onChange={() => console.log('change filter')}
+          filtersChanged={value => this.handleFilterChange(value)}
+          groupBysChanged={value => this.handleGroupBysChange(value)}
           target={target}
           uiSegmentSrv={uiSegmentSrv}
           labelData={labelData}
           templateSrv={templateSrv}
           loading={loadLabelsPromise}
         />
+        <AggregationPicker
+          valueType={valueType}
+          metricKind={metricKind}
+          templateSrv={templateSrv}
+          aggregation={aggregation}
+          onChange={value => this.handleAggregationChange(value)}
+        />
         {/* target="ctrl.target" refresh="ctrl.refresh()" loading="ctrl.loadLabelsPromise" label-data="ctrl.labelData" */}
         {/* <stackdriver-filter
           target="target"

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

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

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

@@ -1,5 +1,5 @@
 <query-editor-row query-ctrl="ctrl" has-text-edit-mode="false">
-    <query-editor target="ctrl.target" template-srv="ctrl.templateSrv" datasource="ctrl.datasource" on-change="ctrl.handleTargetChange" ui-segment-srv=ctrl.uiSegmentSrv></query-editor>
+    <query-editor target="ctrl.target" template-srv="ctrl.templateSrv" datasource="ctrl.datasource" on-query-change="ctrl.handleQueryChange" on-execute-query="ctrl.handleExecuteQuery" 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>

+ 29 - 27
public/app/plugins/datasource/stackdriver/partials/query.filter.html

@@ -1,31 +1,33 @@
-<div class="gf-form-inline">
-  <div class="gf-form">
-    <span class="gf-form-label width-9 query-keyword">Service</span>
-    <stackdriver-picker
-      onChange="ctrl.handleServiceChange"
-      selected="ctrl.target.service"
-      options="ctrl.services"
-      searchable="false"
-      placeholder="'Select Services'"
-      className="'width-15'"
-    ></stackdriver-picker>
-  </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 query-keyword">Metric</span>
-    <stackdriver-picker
-      onChange="ctrl.handleMetricTypeChange"
-      selected="ctrl.target.metricType"
-      options="ctrl.getMetricsList()"
-      template-variables="ctrl.templateSrv.variables"
-      group-name="'Metric Types'"
-      searchable="true"
-      placeholder="'Select Metric'"
-      className="'width-15'"
-    ></stackdriver-picker>
+<!--
+  <div class="gf-form-inline">
+    <div class="gf-form">
+      <span class="gf-form-label width-9 query-keyword">Service</span>
+      <stackdriver-picker
+        onChange="ctrl.handleServiceChange"
+        selected="ctrl.target.service"
+        options="ctrl.services"
+        searchable="false"
+        placeholder="'Select Services'"
+        className="'width-15'"
+      ></stackdriver-picker>
+    </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 query-keyword">Metric</span>
+      <stackdriver-picker
+        onChange="ctrl.handleMetricTypeChange"
+        selected="ctrl.target.metricType"
+        options="ctrl.getMetricsList()"
+        template-variables="ctrl.templateSrv.variables"
+        group-name="'Metric Types'"
+        searchable="true"
+        placeholder="'Select Metric'"
+        className="'width-15'"
+      ></stackdriver-picker>
+    </div>
+-->
 <div class="gf-form-inline">
   <div class="gf-form">
     <span class="gf-form-label query-keyword width-9">Filter</span>

+ 6 - 32
public/app/plugins/datasource/stackdriver/query_ctrl.ts

@@ -1,5 +1,4 @@
 import _ from 'lodash';
-import appEvents from 'app/core/app_events';
 import { QueryCtrl } from 'app/plugins/sdk';
 import './query_aggregation_ctrl';
 import './query_filter_ctrl';
@@ -80,42 +79,17 @@ export class StackdriverQueryCtrl extends QueryCtrl {
       'groupName',
       ['templateVariables', { watchDepth: 'reference' }],
     ]);
-    // this.handleMetricTypeChange = this.handleMetricTypeChange.bind(this);
-    // this.handleAggregationChange = this.handleAggregationChange.bind(this);
-    this.handleTargetChange = this.handleTargetChange.bind(this);
     registerAngularDirectives();
-    // this.getLabels();
+    this.handleQueryChange = this.handleQueryChange.bind(this);
+    this.handleExecuteQuery = this.handleExecuteQuery.bind(this);
   }
 
-  // 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);
+  handleQueryChange(target: Target) {
+    Object.assign(this.target, target);
   }
 
-  async getLabels() {
-    this.loadLabelsPromise = new Promise(async resolve => {
-      try {
-        const { meta } = await this.datasource.getLabels(this.target.metricType, this.target.refId);
-        this.labelData = meta;
-        resolve();
-      } catch (error) {
-        appEvents.emit('alert-error', ['Error', 'Error loading metric labels for ' + this.target.metricType]);
-        resolve();
-      }
-    });
+  handleExecuteQuery() {
+    this.$scope.ctrl.refresh();
   }
 
   onDataReceived(dataList) {

+ 19 - 133
public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts

@@ -1,8 +1,6 @@
 import coreModule from 'app/core/core_module';
 import _ from 'lodash';
-import { FilterSegments } from './filter_segments';
-import { QueryMeta } from './types';
-// import appEvents from 'app/core/app_events';
+import { FilterSegments, DefaultFilterValue } from './filter_segments';
 
 export class StackdriverFilter {
   /** @ngInject */
@@ -16,7 +14,8 @@ export class StackdriverFilter {
         labelData: '<',
         loading: '<',
         target: '=',
-        refresh: '&',
+        filtersChanged: '&',
+        groupBysChanged: '&',
         hideGroupBys: '<',
       },
     };
@@ -24,41 +23,19 @@ export class StackdriverFilter {
 }
 
 export class StackdriverFilterCtrl {
-  metricLabels: { [key: string]: string[] };
-  resourceLabels: { [key: string]: string[] };
-  resourceTypes: string[];
-
   defaultRemoveGroupByValue = '-- remove group by --';
   resourceTypeValue = 'resource.type';
-
-  metricDescriptors: any[];
-  metrics: any[];
-  services: any[];
   groupBySegments: any[];
   filterSegments: FilterSegments;
   removeSegment: any;
   target: any;
 
-  labelData: QueryMeta;
-  loading: Promise<any>;
-
   /** @ngInject */
   constructor(private $scope, private uiSegmentSrv, private templateSrv, private $rootScope) {
-    this.target = $scope.target;
-    this.metricDescriptors = [];
-    this.metrics = [];
-    this.services = [];
-
-    this.initSegments($scope.hideGroupBys);
-    this.handleMetricTypeChange = this.handleMetricTypeChange.bind(this);
-    this.handleServiceChange = this.handleServiceChange.bind(this);
-  }
+    this.$scope = $scope.$parent;
+    this.target = this.$scope.target;
 
-  handleMetricTypeChange(value) {
-    this.target.metricType = value;
-    this.setMetricType();
-    this.$scope.refresh();
-    // this.getLabels();
+    this.initSegments(this.$scope.hideGroupBys);
   }
 
   initSegments(hideGroupBys: boolean) {
@@ -71,106 +48,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();
-  }
-
-  // async getCurrentProject() {
-  //   return new Promise(async (resolve, reject) => {
-  //     try {
-  //       if (!this.target.defaultProject || this.target.defaultProject === 'loading project...') {
-  //         this.target.defaultProject = await this.datasource.getDefaultProject();
-  //       }
-  //       resolve(this.target.defaultProject);
-  //     } catch (error) {
-  //       appEvents.emit('ds-request-error', error);
-  //       reject();
-  //     }
-  //   });
-  // }
-
-  // async loadMetricDescriptors() {
-  //   if (this.target.defaultProject !== 'loading project...') {
-  //     this.metricDescriptors = await this.datasource.getMetricTypes(this.target.defaultProject);
-  //     this.services = this.getServicesList();
-  //     this.metrics = this.getMetricsList();
-  //     return this.metricDescriptors;
-  //   } else {
-  //     return [];
-  //   }
-  // }
-
-  getServicesList() {
-    const services = this.metricDescriptors.map(m => ({
-      value: m.service,
-      label: _.startCase(m.serviceShortName),
-    }));
-
-    return services.length > 0 ? _.uniqBy(services, s => s.value) : [];
-  }
-
-  getMetricsList() {
-    const metricsByService = this.metricDescriptors.filter(m => m.service === this.target.service).map(m => ({
-      service: m.service,
-      value: m.type,
-      label: m.displayName,
-      description: m.description,
-    }));
-
-    if (
-      metricsByService.length > 0 &&
-      !metricsByService.some(m => m.value === this.templateSrv.replace(this.target.metricType))
-    ) {
-      this.target.metricType = metricsByService[0].value;
-    }
-    return metricsByService;
-  }
-
-  // async getLabels() {
-  //   this.loadLabelsPromise = new Promise(async resolve => {
-  //     try {
-  //       if (this.target.metricType) {
-  //         const { meta } = await this.datasource.getLabels(this.target.metricType, this.target.refId);
-  //         this.metricLabels = meta.metricLabels;
-  //         this.resourceLabels = meta.resourceLabels;
-  //         this.resourceTypes = meta.resourceTypes;
-  //         resolve();
-  //       } else {
-  //         resolve();
-  //       }
-  //     } catch (error) {
-  //       appEvents.emit('alert-error', ['Error', 'Error loading metric labels for ' + this.target.metricType]);
-  //       resolve();
-  //     }
-  //   });
-  // }
-
-  handleServiceChange(service) {
-    this.target.service = service;
-    this.metrics = this.getMetricsList();
-    this.setMetricType();
-    // this.getLabels();
-    if (!this.metrics.some(m => m.value === this.target.metricType)) {
-      this.target.metricType = '';
-    } else {
-      this.$scope.refresh();
-    }
-  }
-
-  setMetricType() {
-    const metric = this.metricDescriptors.find(m => m.type === this.templateSrv.replace(this.target.metricType));
-    if (metric) {
-      const { valueType, metricKind, unit } = metric;
-      this.target.unit = unit;
-      this.target.valueType = valueType;
-      this.target.metricKind = metricKind;
-      this.$rootScope.$broadcast('metricTypeChanged');
-    }
+    this.filterSegments = new FilterSegments(
+      this.uiSegmentSrv,
+      this.target,
+      this.getFilterKeys.bind(this),
+      this.getFilterValues.bind(this)
+    );
+    this.filterSegments.buildSegmentModel();
   }
 
   async createLabelKeyElements() {
@@ -251,10 +135,10 @@ export class StackdriverFilterCtrl {
       return memo;
     };
 
-    this.target.aggregation.groupBys = this.groupBySegments.reduce(reducer, []);
+    const groupBys = this.groupBySegments.reduce(reducer, []);
+    this.$scope.groupBysChanged(groupBys);
     this.ensurePlusButton(this.groupBySegments);
     this.$rootScope.$broadcast('metricTypeChanged');
-    this.$scope.refresh();
   }
 
   async getFilters(segment, index) {
@@ -291,8 +175,10 @@ export class StackdriverFilterCtrl {
   }
 
   filterSegmentUpdated(segment, index) {
-    this.target.filters = this.filterSegments.filterSegmentUpdated(segment, index);
-    this.$scope.refresh();
+    const filters = this.filterSegments.filterSegmentUpdated(segment, index);
+    if (!filters.some(f => f === DefaultFilterValue)) {
+      this.$scope.filtersChanged(filters);
+    }
   }
 
   ensurePlusButton(segments) {