stackdriver: add support for template variables

This commit is contained in:
Erik Sundell 2018-11-19 14:44:40 +01:00
parent 26b1cc5dcf
commit 0aeaec1ac6
7 changed files with 64 additions and 34 deletions

View File

@ -14,7 +14,7 @@ async function loadComponent(module) {
} }
/** @ngInject */ /** @ngInject */
function variableQueryEditorLoader() { function variableQueryEditorLoader(templateSrv) {
return { return {
restrict: 'E', restrict: 'E',
link: async (scope, elem) => { link: async (scope, elem) => {
@ -23,6 +23,7 @@ function variableQueryEditorLoader() {
datasource: scope.currentDatasource, datasource: scope.currentDatasource,
query: scope.current.query, query: scope.current.query,
onChange: scope.onQueryChange, onChange: scope.onQueryChange,
templateSrv,
}; };
ReactDOM.render(<Component {...props} />, elem[0]); ReactDOM.render(<Component {...props} />, elem[0]);
scope.$on('$destroy', () => { scope.$on('$destroy', () => {

View File

@ -1,7 +1,12 @@
import isString from 'lodash/isString'; import isString from 'lodash/isString';
import { alignmentPeriods } from './constants'; import { alignmentPeriods } from './constants';
import { MetricFindQueryTypes } from './types'; import { MetricFindQueryTypes } from './types';
import { getMetricTypesByService, getAlignmentOptionsByMetric, getAggregationOptionsByMetric } from './functions'; import {
getMetricTypesByService,
getAlignmentOptionsByMetric,
getAggregationOptionsByMetric,
getLabelKeys,
} from './functions';
export default class StackdriverMetricFindQuery { export default class StackdriverMetricFindQuery {
constructor(private datasource) {} constructor(private datasource) {}
@ -37,37 +42,34 @@ export default class StackdriverMetricFindQuery {
return []; return [];
} }
const metricDescriptors = await this.datasource.getMetricTypes(this.datasource.projectName); const metricDescriptors = await this.datasource.getMetricTypes(this.datasource.projectName);
return getMetricTypesByService(metricDescriptors, selectedService).map(s => ({ return getMetricTypesByService(metricDescriptors, this.datasource.templateSrv.replace(selectedService)).map(s => ({
text: s.displayName, text: s.displayName,
value: s.type, value: s.type,
expandable: true, expandable: true,
})); }));
} }
async handleLabelKeysQuery({ selectedQueryType, selectedMetricType, labelKey }) { async handleLabelKeysQuery({ selectedMetricType }) {
if (!selectedMetricType) { if (!selectedMetricType) {
return []; return [];
} }
const refId = 'handleLabelKeysQuery'; const labelKeys = await getLabelKeys(this.datasource, selectedMetricType);
const response = await this.datasource.getLabels(selectedMetricType, refId);
const labelKeys = response.meta
? [...Object.keys(response.meta.resourceLabels), ...Object.keys(response.meta.metricLabels)]
: [];
return labelKeys.map(this.toFindQueryResult); return labelKeys.map(this.toFindQueryResult);
} }
async handleLabelValuesQuery({ selectedQueryType, selectedMetricType, labelKey }) { async handleLabelValuesQuery({ selectedMetricType, labelKey }) {
if (!selectedMetricType) { if (!selectedMetricType) {
return []; return [];
} }
const refId = 'handleLabelValuesQuery'; const refId = 'handleLabelValuesQuery';
const response = await this.datasource.getLabels(selectedMetricType, refId); const response = await this.datasource.getLabels(selectedMetricType, refId);
const interpolatedKey = this.datasource.templateSrv.replace(labelKey);
const [name] = interpolatedKey.split('.').reverse();
let values = []; let values = [];
if (response.meta && response.meta.metricLabels && response.meta.metricLabels.hasOwnProperty(labelKey)) { if (response.meta && response.meta.metricLabels && response.meta.metricLabels.hasOwnProperty(name)) {
values = response.meta.metricLabels[labelKey]; values = response.meta.metricLabels[name];
} else if (response.meta && response.meta.resourceLabels && response.meta.resourceLabels.hasOwnProperty(labelKey)) { } else if (response.meta && response.meta.resourceLabels && response.meta.resourceLabels.hasOwnProperty(name)) {
values = response.meta.resourceLabels[labelKey]; values = response.meta.resourceLabels[name];
} }
return values.map(this.toFindQueryResult); return values.map(this.toFindQueryResult);
@ -87,7 +89,9 @@ export default class StackdriverMetricFindQuery {
return []; return [];
} }
const metricDescriptors = await this.datasource.getMetricTypes(this.datasource.projectName); const metricDescriptors = await this.datasource.getMetricTypes(this.datasource.projectName);
const { valueType, metricKind } = metricDescriptors.find(m => m.type === selectedMetricType); const { valueType, metricKind } = metricDescriptors.find(
m => m.type === this.datasource.templateSrv.replace(selectedMetricType)
);
return getAlignmentOptionsByMetric(valueType, metricKind).map(this.toFindQueryResult); return getAlignmentOptionsByMetric(valueType, metricKind).map(this.toFindQueryResult);
} }
@ -96,7 +100,9 @@ export default class StackdriverMetricFindQuery {
return []; return [];
} }
const metricDescriptors = await this.datasource.getMetricTypes(this.datasource.projectName); const metricDescriptors = await this.datasource.getMetricTypes(this.datasource.projectName);
const { valueType, metricKind } = metricDescriptors.find(m => m.type === selectedMetricType); const { valueType, metricKind } = metricDescriptors.find(
m => m.type === this.datasource.templateSrv.replace(selectedMetricType)
);
return getAggregationOptionsByMetric(valueType, metricKind).map(this.toFindQueryResult); return getAggregationOptionsByMetric(valueType, metricKind).map(this.toFindQueryResult);
} }

View File

@ -14,6 +14,7 @@ const props: VariableQueryProps = {
datasource: { datasource: {
getMetricTypes: async p => [], getMetricTypes: async p => [],
}, },
templateSrv: { replace: s => s },
}; };
describe('VariableQueryEditor', () => { describe('VariableQueryEditor', () => {

View File

@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
import uniqBy from 'lodash/uniqBy'; import uniqBy from 'lodash/uniqBy';
import { VariableQueryProps } from 'app/types/plugins'; import { VariableQueryProps } from 'app/types/plugins';
import SimpleSelect from './SimpleSelect'; import SimpleSelect from './SimpleSelect';
import { getMetricTypes } from '../functions'; import { getMetricTypes, getLabelKeys } from '../functions';
import { MetricFindQueryTypes, VariableQueryData } from '../types'; import { MetricFindQueryTypes, VariableQueryData } from '../types';
export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryProps, VariableQueryData> { export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryProps, VariableQueryData> {
@ -40,7 +40,7 @@ export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryP
})); }));
let selectedService = ''; let selectedService = '';
if (services.some(s => s.value === this.state.selectedService)) { if (services.some(s => s.value === this.props.templateSrv.replace(this.state.selectedService))) {
selectedService = this.state.selectedService; selectedService = this.state.selectedService;
} else if (services && services.length > 0) { } else if (services && services.length > 0) {
selectedService = services[0].value; selectedService = services[0].value;
@ -49,6 +49,7 @@ export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryP
const { metricTypes, selectedMetricType } = getMetricTypes( const { metricTypes, selectedMetricType } = getMetricTypes(
metricDescriptors, metricDescriptors,
this.state.selectedMetricType, this.state.selectedMetricType,
this.props.templateSrv.replace(this.state.selectedMetricType),
selectedService selectedService
); );
const state: any = { const state: any = {
@ -57,7 +58,7 @@ export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryP
metricTypes, metricTypes,
selectedMetricType, selectedMetricType,
metricDescriptors, metricDescriptors,
...await this.getLabelValues(selectedMetricType), ...await this.getLabels(selectedMetricType),
}; };
this.setState(state); this.setState(state);
} }
@ -65,7 +66,7 @@ export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryP
async handleQueryTypeChange(event) { async handleQueryTypeChange(event) {
const state: any = { const state: any = {
selectedQueryType: event.target.value, selectedQueryType: event.target.value,
...await this.getLabelValues(this.state.selectedMetricType, event.target.value), ...await this.getLabels(this.state.selectedMetricType, event.target.value),
}; };
this.setState(state); this.setState(state);
} }
@ -74,19 +75,20 @@ export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryP
const { metricTypes, selectedMetricType } = getMetricTypes( const { metricTypes, selectedMetricType } = getMetricTypes(
this.state.metricDescriptors, this.state.metricDescriptors,
this.state.selectedMetricType, this.state.selectedMetricType,
this.props.templateSrv.replace(this.state.selectedMetricType),
event.target.value event.target.value
); );
const state: any = { const state: any = {
selectedService: event.target.value, selectedService: event.target.value,
metricTypes, metricTypes,
selectedMetricType, selectedMetricType,
...await this.getLabelValues(selectedMetricType), ...await this.getLabels(selectedMetricType),
}; };
this.setState(state); this.setState(state);
} }
async onMetricTypeChange(event) { async onMetricTypeChange(event) {
const state: any = { selectedMetricType: event.target.value, ...await this.getLabelValues(event.target.value) }; const state: any = { selectedMetricType: event.target.value, ...await this.getLabels(event.target.value) };
this.setState(state); this.setState(state);
} }
@ -100,18 +102,23 @@ export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryP
this.props.onChange(queryModel, `Stackdriver - ${query.name}`); this.props.onChange(queryModel, `Stackdriver - ${query.name}`);
} }
async getLabelValues(selectedMetricType, selectedQueryType = this.state.selectedQueryType) { async getLabels(selectedMetricType, selectedQueryType = this.state.selectedQueryType) {
let result = { labels: this.state.labels, labelKey: this.state.labelKey }; let result = { labels: this.state.labels, labelKey: this.state.labelKey };
if (selectedMetricType && selectedQueryType === MetricFindQueryTypes.LabelValues) { if (selectedMetricType && selectedQueryType === MetricFindQueryTypes.LabelValues) {
const refId = 'StackdriverVariableQueryEditor'; const labels = await getLabelKeys(this.props.datasource, selectedMetricType);
const response = await this.props.datasource.getLabels(selectedMetricType, refId); const labelKey = labels.some(l => l === this.props.templateSrv.replace(this.state.labelKey))
const labels = [...Object.keys(response.meta.resourceLabels), ...Object.keys(response.meta.metricLabels)]; ? this.state.labelKey
const labelKey = labels.some(l => l === this.state.labelKey) ? this.state.labelKey : labels[0]; : labels[0];
result = { labels, labelKey }; result = { labels, labelKey };
} }
return result; return result;
} }
insertTemplateVariables(options) {
const templateVariables = this.props.templateSrv.variables.map(v => ({ name: `$${v.name}`, value: `$${v.name}` }));
return [...templateVariables, ...options];
}
renderQueryTypeSwitch(queryType) { renderQueryTypeSwitch(queryType) {
switch (queryType) { switch (queryType) {
case MetricFindQueryTypes.MetricTypes: case MetricFindQueryTypes.MetricTypes:
@ -136,14 +143,14 @@ export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryP
/> />
<SimpleSelect <SimpleSelect
value={this.state.selectedMetricType} value={this.state.selectedMetricType}
options={this.state.metricTypes} options={this.insertTemplateVariables(this.state.metricTypes)}
onValueChange={e => this.onMetricTypeChange(e)} onValueChange={e => this.onMetricTypeChange(e)}
label="Metric Types" label="Metric Types"
/> />
{queryType === MetricFindQueryTypes.LabelValues && ( {queryType === MetricFindQueryTypes.LabelValues && (
<SimpleSelect <SimpleSelect
value={this.state.labelKey} value={this.state.labelKey}
options={this.state.labels.map(l => ({ value: l, name: l }))} options={this.insertTemplateVariables(this.state.labels.map(l => ({ value: l, name: l })))}
onValueChange={e => this.onLabelKeyChange(e)} onValueChange={e => this.onLabelKeyChange(e)}
label="Label Keys" label="Label Keys"
/> />
@ -162,7 +169,7 @@ export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryP
/> />
<SimpleSelect <SimpleSelect
value={this.state.selectedMetricType} value={this.state.selectedMetricType}
options={this.state.metricTypes} options={this.insertTemplateVariables(this.state.metricTypes)}
onValueChange={e => this.onMetricTypeChange(e)} onValueChange={e => this.onMetricTypeChange(e)}
label="Metric Types" label="Metric Types"
/> />

View File

@ -3,12 +3,12 @@ import { alignOptions, aggOptions } from './constants';
export const getMetricTypesByService = (metricDescriptors, service) => export const getMetricTypesByService = (metricDescriptors, service) =>
metricDescriptors.filter(m => m.service === service); metricDescriptors.filter(m => m.service === service);
export const getMetricTypes = (metricDescriptors, metricType, selectedService) => { export const getMetricTypes = (metricDescriptors, metricType, interpolatedMetricType, selectedService) => {
const metricTypes = getMetricTypesByService(metricDescriptors, selectedService).map(m => ({ const metricTypes = getMetricTypesByService(metricDescriptors, selectedService).map(m => ({
value: m.type, value: m.type,
name: m.displayName, name: m.displayName,
})); }));
const metricTypeExistInArray = metricTypes.some(m => m.value === metricType); const metricTypeExistInArray = metricTypes.some(m => m.value === interpolatedMetricType);
const selectedMetricType = metricTypeExistInArray ? metricType : metricTypes[0].value; const selectedMetricType = metricTypeExistInArray ? metricType : metricTypes[0].value;
return { return {
metricTypes, metricTypes,
@ -31,3 +31,15 @@ export const getAggregationOptionsByMetric = (valueType, metricKind) => {
return i.valueTypes.indexOf(valueType) !== -1 && i.metricKinds.indexOf(metricKind) !== -1; return i.valueTypes.indexOf(valueType) !== -1 && i.metricKinds.indexOf(metricKind) !== -1;
}); });
}; };
export const getLabelKeys = async (datasource, selectedMetricType) => {
const refId = 'handleLabelKeysQuery';
const response = await datasource.getLabels(selectedMetricType, refId);
const labelKeys = response.meta
? [
...Object.keys(response.meta.resourceLabels).map(l => `resource.label.${l}`),
...Object.keys(response.meta.metricLabels).map(l => `metric.label.${l}`),
]
: [];
return labelKeys;
};

View File

@ -65,7 +65,9 @@ export class StackdriverAggregationCtrl {
const selectedAlignment = this.alignOptions.find( const selectedAlignment = this.alignOptions.find(
ap => ap.value === this.templateSrv.replace(this.target.aggregation.perSeriesAligner) ap => ap.value === this.templateSrv.replace(this.target.aggregation.perSeriesAligner)
); );
return `${kbn.secondsToHms(this.$scope.alignmentPeriod)} interval (${selectedAlignment.text})`; return `${kbn.secondsToHms(this.$scope.alignmentPeriod)} interval (${
selectedAlignment ? selectedAlignment.text : ''
})`;
} }
deselectAggregationOption(notValidOptionValue: string) { deselectAggregationOption(notValidOptionValue: string) {

View File

@ -104,4 +104,5 @@ export interface VariableQueryProps {
query: any; query: any;
onChange: (query: any, definition: string) => void; onChange: (query: any, definition: string) => void;
datasource: any; datasource: any;
templateSrv: any;
} }