Submit
Path:
~
/
/
usr
/
share
/
grafana
/
public
/
app
/
plugins
/
datasource
/
cloud-monitoring
/
components
/
File Content:
VisualMetricQueryEditor.tsx
import { css } from '@emotion/css'; import debounce from 'debounce-promise'; import { startCase, uniqBy } from 'lodash'; import React, { useCallback, useEffect, useState } from 'react'; import { GrafanaTheme2, SelectableValue, TimeRange } from '@grafana/data'; import { EditorField, EditorFieldGroup, EditorRow } from '@grafana/experimental'; import { reportInteraction } from '@grafana/runtime'; import { getSelectStyles, Select, AsyncSelect, useStyles2, useTheme2 } from '@grafana/ui'; import CloudMonitoringDatasource from '../datasource'; import { selectors } from '../e2e/selectors'; import { getAlignmentPickerData, getMetricType, setMetricType } from '../functions'; import { PreprocessorType, TimeSeriesList, MetricKind, ValueTypes } from '../types/query'; import { CustomMetaData, MetricDescriptor } from '../types/types'; import { AliasBy } from './AliasBy'; import { Alignment } from './Alignment'; import { GroupBy } from './GroupBy'; import { LabelFilter } from './LabelFilter'; import { defaultTimeSeriesList } from './MetricQueryEditor'; import { Preprocessor } from './Preprocessor'; import { Project } from './Project'; export interface Props { refId: string; customMetaData: CustomMetaData; onChange: (query: TimeSeriesList) => void; datasource: CloudMonitoringDatasource; query: TimeSeriesList; variableOptionGroup: SelectableValue<string>; aliasBy?: string; onChangeAliasBy: (aliasBy: string) => void; range: TimeRange; } export function Editor({ refId, onChange, datasource, query, variableOptionGroup, customMetaData, aliasBy, onChangeAliasBy, range, }: React.PropsWithChildren<Props>) { const [labels, setLabels] = useState<{ [k: string]: string[] }>({}); const [metricDescriptors, setMetricDescriptors] = useState<MetricDescriptor[]>([]); const [metricDescriptor, setMetricDescriptor] = useState<MetricDescriptor>(); const [metrics, setMetrics] = useState<Array<SelectableValue<string>>>([]); const [services, setServices] = useState<Array<SelectableValue<string>>>([]); const [service, setService] = useState<string>(''); const [timeRange, setTimeRange] = useState<TimeRange>({ ...range }); const useTime = (time: TimeRange) => { if ( timeRange !== null && (timeRange.raw.from.toString() !== time.raw.from.toString() || timeRange.raw.to.toString() !== time.raw.to.toString()) ) { setTimeRange({ ...time }); } }; useTime(range); const theme = useTheme2(); const selectStyles = getSelectStyles(theme); const customStyle = useStyles2(getStyles); const { projectName, groupBys, crossSeriesReducer } = query; const metricType = getMetricType(query); const { templateSrv } = datasource; const getSelectedMetricDescriptor = useCallback( (metricDescriptors: MetricDescriptor[], metricType: string) => { return metricDescriptors.find((md) => md.type === templateSrv.replace(metricType))!; }, [templateSrv] ); useEffect(() => { if (projectName && metricType) { datasource .getLabels(metricType, refId, projectName, { groupBys, crossSeriesReducer }, timeRange) .then((labels) => setLabels(labels)); } }, [datasource, groupBys, metricType, projectName, refId, crossSeriesReducer, timeRange]); useEffect(() => { const loadMetricDescriptors = async () => { if (projectName) { const metricDescriptors = await datasource.getMetricTypes(projectName); reportInteraction('cloud-monitoring-metric-descriptors-loaded', { count: metricDescriptors.length, }); const services = getServicesList(metricDescriptors); setMetricDescriptors(metricDescriptors); setServices(services); } }; loadMetricDescriptors(); }, [datasource, projectName, customStyle, selectStyles.optionDescription]); useEffect(() => { const getMetricsList = (metricDescriptors: MetricDescriptor[]) => { const selectedMetricDescriptor = getSelectedMetricDescriptor(metricDescriptors, metricType); if (!selectedMetricDescriptor) { return []; } const metricsByService = metricDescriptors .filter((m) => m.service === selectedMetricDescriptor.service) .map((m) => ({ service: m.service, value: m.type, label: m.displayName, component: function optionComponent() { return ( <div> <div className={customStyle}>{m.type}</div> <div className={selectStyles.optionDescription}>{m.description}</div> </div> ); }, })); return metricsByService; }; const metrics = getMetricsList(metricDescriptors); const service = metrics.length > 0 ? metrics[0].service : ''; const metricDescriptor = getSelectedMetricDescriptor(metricDescriptors, metricType); setMetricDescriptor(metricDescriptor); setMetrics(metrics); setService(service); }, [metricDescriptors, getSelectedMetricDescriptor, metricType, customStyle, selectStyles.optionDescription]); const onServiceChange = ({ value: service }: SelectableValue<string>) => { const metrics = metricDescriptors .filter((m: MetricDescriptor) => m.service === templateSrv.replace(service)) .map((m: MetricDescriptor) => ({ service: m.service, value: m.type, label: m.displayName, description: m.description, })); // On service change reset all query values except the project name query.filters = []; if (metrics.length > 0 && !metrics.some((m) => m.value === templateSrv.replace(metricType))) { onMetricTypeChange(metrics[0]); setService(service!); setMetrics(metrics); } else { setService(service!); setMetrics(metrics); } }; const getServicesList = (metricDescriptors: MetricDescriptor[]) => { const services = metricDescriptors.map((m) => ({ value: m.service, label: startCase(m.serviceShortName), })); return services.length > 0 ? uniqBy(services, (s) => s.value) : []; }; const filterMetrics = async (filter: string) => { const metrics = await datasource.filterMetricsByType(projectName, service); const filtered = metrics .filter((m) => m.type.includes(filter.toLowerCase())) .map((m) => ({ value: m.type, label: m.displayName, component: function optionComponent() { return ( <div> <div className={customStyle}>{m.type}</div> <div className={selectStyles.optionDescription}>{m.description}</div> </div> ); }, })); return [ { label: 'Template Variables', options: variableOptionGroup.options, }, ...filtered, ]; }; const debounceFilter = debounce(filterMetrics, 400); const onMetricTypeChange = ({ value }: SelectableValue<string>) => { const metricDescriptor = getSelectedMetricDescriptor(metricDescriptors, value!); setMetricDescriptor(metricDescriptor); const { metricKind, valueType } = metricDescriptor; const preprocessor = metricKind === MetricKind.GAUGE || valueType === ValueTypes.DISTRIBUTION ? PreprocessorType.None : PreprocessorType.Rate; const { perSeriesAligner } = getAlignmentPickerData(valueType, metricKind, query.perSeriesAligner, preprocessor); // On metric name change reset query to defaults except project name and filters Object.assign(query, { ...defaultTimeSeriesList(datasource), projectName: query.projectName, filters: query.filters, }); onChange({ ...setMetricType( { ...query, perSeriesAligner, }, value! ), preprocessor, }); }; return ( <span data-testid={selectors.components.queryEditor.visualMetricsQueryEditor.container.input}> <EditorRow> <EditorFieldGroup> <Project refId={refId} templateVariableOptions={variableOptionGroup.options} projectName={projectName} datasource={datasource} onChange={(projectName) => { onChange({ ...query, projectName }); }} /> <EditorField label="Service" width="auto"> <Select width="auto" onChange={onServiceChange} isLoading={services.length === 0} value={[...services, ...variableOptionGroup.options].find((s) => s.value === service)} options={[ { label: 'Template Variables', options: variableOptionGroup.options, }, ...services, ]} placeholder="Select Services" inputId={`${refId}-service`} /> </EditorField> <EditorField label="Metric name" width="auto" htmlFor={`${refId}-select-metric`}> <span title={service === '' ? 'Select a service first' : 'Type to search metrics'}> <AsyncSelect width="auto" onChange={onMetricTypeChange} value={[...metrics, ...variableOptionGroup.options].find((s) => s.value === metricType)} loadOptions={debounceFilter} defaultOptions={[ { label: 'Template Variables', options: variableOptionGroup.options, }, ...metrics.slice(0, 100), ]} placeholder="Select Metric" inputId={`${refId}-select-metric`} disabled={service === ''} /> </span> </EditorField> </EditorFieldGroup> </EditorRow> <> <LabelFilter labels={labels} filters={query.filters!} onChange={(filters: string[]) => onChange({ ...query, filters })} variableOptionGroup={variableOptionGroup} /> <EditorRow> <Preprocessor metricDescriptor={metricDescriptor} query={query} onChange={onChange} /> <GroupBy refId={refId} labels={Object.keys(labels)} query={query} onChange={onChange} variableOptionGroup={variableOptionGroup} metricDescriptor={metricDescriptor} /> <Alignment refId={refId} datasource={datasource} templateVariableOptions={variableOptionGroup.options} query={query} customMetaData={customMetaData} onChange={onChange} metricDescriptor={metricDescriptor} preprocessor={query.preprocessor} /> <AliasBy refId={refId} value={aliasBy} onChange={onChangeAliasBy} /> </EditorRow> </> </span> ); } const getStyles = (theme: GrafanaTheme2) => css` label: grafana-select-option-description; font-weight: normal; font-style: italic; color: ${theme.colors.text.secondary}; `; export const VisualMetricQueryEditor = React.memo(Editor);
Edit
Rename
Chmod
Delete
FILE
FOLDER
INFO
Name
Size
Permission
Action
ConfigEditor
---
0755
__snapshots__
---
0755
Aggregation.test.tsx
1918 bytes
0644
Aggregation.tsx
2200 bytes
0644
AliasBy.tsx
756 bytes
0644
Alignment.test.tsx
2970 bytes
0644
Alignment.tsx
2243 bytes
0644
AlignmentFunction.tsx
1572 bytes
0644
AnnotationQueryEditor.test.tsx
2690 bytes
0644
AnnotationQueryEditor.tsx
2974 bytes
0644
AnnotationsHelp.tsx
1642 bytes
0644
CloudMonitoringCheatSheet.tsx
3001 bytes
0644
Fields.tsx
675 bytes
0644
GraphPeriod.test.tsx
1244 bytes
0644
GraphPeriod.tsx
1499 bytes
0644
GroupBy.test.tsx
1410 bytes
0644
GroupBy.tsx
2128 bytes
0644
LabelFilter.test.tsx
4714 bytes
0644
LabelFilter.tsx
4440 bytes
0644
LookbackPeriodSelect.tsx
1440 bytes
0644
MQLQueryEditor.tsx
975 bytes
0644
MetricQueryEditor.test.tsx
4054 bytes
0644
MetricQueryEditor.tsx
5064 bytes
0644
PeriodSelect.tsx
1386 bytes
0644
Preprocessor.test.tsx
4657 bytes
0644
Preprocessor.tsx
2569 bytes
0644
Project.tsx
1423 bytes
0644
PromQLEditor.tsx
2449 bytes
0644
QueryEditor.test.tsx
5229 bytes
0644
QueryEditor.tsx
5193 bytes
0644
QueryHeader.test.tsx
1897 bytes
0644
QueryHeader.tsx
826 bytes
0644
SLO.tsx
1793 bytes
0644
SLOQueryEditor.tsx
3769 bytes
0644
Selector.tsx
1214 bytes
0644
Service.tsx
1603 bytes
0644
VariableQueryEditor.test.tsx
2434 bytes
0644
VariableQueryEditor.tsx
11903 bytes
0644
VisualMetricQueryEditor.test.tsx
11495 bytes
0644
VisualMetricQueryEditor.tsx
11179 bytes
0644
index.ts
721 bytes
0644
N4ST4R_ID | Naxtarrr